@things-factory/dataset 5.0.0-alpha.3 → 5.0.0-alpha.32

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 (99) hide show
  1. package/README.md +13 -0
  2. package/assets/data-samples.jpg +0 -0
  3. package/client/bootstrap.js +16 -1
  4. package/client/pages/data-entry/data-entry-form.js +85 -0
  5. package/client/pages/data-entry/data-entry-list-page.js +464 -0
  6. package/client/pages/data-ooc/data-ooc-list-page.js +488 -0
  7. package/client/pages/data-ooc/data-ooc-view.js +182 -0
  8. package/client/pages/{data-sample.js → data-sample/data-sample-list-page.js} +153 -59
  9. package/client/pages/data-sample/data-sample-view.js +97 -0
  10. package/client/pages/{data-sensor.js → data-sensor/data-sensor-list-page.js} +11 -16
  11. package/client/pages/{data-item-list.js → data-set/data-item-list.js} +70 -16
  12. package/client/pages/{data-set-importer.js → data-set/data-set-importer.js} +0 -0
  13. package/client/pages/{data-set.js → data-set/data-set-list-page.js} +282 -76
  14. package/client/route.js +14 -6
  15. package/dist-server/controllers/create-data-sample.js +133 -0
  16. package/dist-server/controllers/create-data-sample.js.map +1 -0
  17. package/dist-server/controllers/data-use-case.js +57 -0
  18. package/dist-server/controllers/data-use-case.js.map +1 -0
  19. package/dist-server/controllers/index.js +17 -0
  20. package/dist-server/controllers/index.js.map +1 -1
  21. package/dist-server/index.js +2 -0
  22. package/dist-server/index.js.map +1 -1
  23. package/dist-server/routes.js +9 -24
  24. package/dist-server/routes.js.map +1 -1
  25. package/dist-server/service/data-item/data-item-mutation.js +5 -1
  26. package/dist-server/service/data-item/data-item-mutation.js.map +1 -1
  27. package/dist-server/service/data-item/data-item-type.js +18 -6
  28. package/dist-server/service/data-item/data-item-type.js.map +1 -1
  29. package/dist-server/service/data-item/data-item.js +27 -7
  30. package/dist-server/service/data-item/data-item.js.map +1 -1
  31. package/dist-server/service/data-ooc/data-ooc-mutation.js +92 -0
  32. package/dist-server/service/data-ooc/data-ooc-mutation.js.map +1 -0
  33. package/dist-server/service/data-ooc/data-ooc-query.js +115 -0
  34. package/dist-server/service/data-ooc/data-ooc-query.js.map +1 -0
  35. package/dist-server/service/data-ooc/data-ooc-subscription.js +65 -0
  36. package/dist-server/service/data-ooc/data-ooc-subscription.js.map +1 -0
  37. package/dist-server/service/data-ooc/data-ooc-type.js +107 -0
  38. package/dist-server/service/data-ooc/data-ooc-type.js.map +1 -0
  39. package/dist-server/service/data-ooc/data-ooc.js +237 -0
  40. package/dist-server/service/data-ooc/data-ooc.js.map +1 -0
  41. package/dist-server/service/data-ooc/index.js +10 -0
  42. package/dist-server/service/data-ooc/index.js.map +1 -0
  43. package/dist-server/service/data-sample/data-sample-mutation.js +3 -105
  44. package/dist-server/service/data-sample/data-sample-mutation.js.map +1 -1
  45. package/dist-server/service/data-sample/data-sample-type.js +12 -42
  46. package/dist-server/service/data-sample/data-sample-type.js.map +1 -1
  47. package/dist-server/service/data-sample/data-sample.js +34 -3
  48. package/dist-server/service/data-sample/data-sample.js.map +1 -1
  49. package/dist-server/service/data-set/data-set-mutation.js +1 -2
  50. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  51. package/dist-server/service/data-set/data-set-query.js +110 -1
  52. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  53. package/dist-server/service/data-set/data-set-type.js +48 -4
  54. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  55. package/dist-server/service/data-set/data-set.js +59 -15
  56. package/dist-server/service/data-set/data-set.js.map +1 -1
  57. package/dist-server/service/data-spec/data-spec-manager.js +20 -0
  58. package/dist-server/service/data-spec/data-spec-manager.js.map +1 -0
  59. package/dist-server/service/data-spec/data-spec-query.js +48 -0
  60. package/dist-server/service/data-spec/data-spec-query.js.map +1 -0
  61. package/dist-server/service/data-spec/data-spec.js +78 -0
  62. package/dist-server/service/data-spec/data-spec.js.map +1 -0
  63. package/dist-server/service/data-spec/index.js +8 -0
  64. package/dist-server/service/data-spec/index.js.map +1 -0
  65. package/dist-server/service/index.js +12 -4
  66. package/dist-server/service/index.js.map +1 -1
  67. package/package.json +18 -12
  68. package/server/controllers/create-data-sample.ts +175 -0
  69. package/server/controllers/data-use-case.ts +85 -0
  70. package/server/controllers/index.ts +1 -0
  71. package/server/index.ts +3 -0
  72. package/server/routes.ts +17 -31
  73. package/server/service/data-item/data-item-mutation.ts +6 -1
  74. package/server/service/data-item/data-item-type.ts +13 -6
  75. package/server/service/data-item/data-item.ts +21 -5
  76. package/server/service/data-ooc/data-ooc-mutation.ts +150 -0
  77. package/server/service/data-ooc/data-ooc-query.ts +63 -0
  78. package/server/service/data-ooc/data-ooc-subscription.ts +51 -0
  79. package/server/service/data-ooc/data-ooc-type.ts +68 -0
  80. package/server/service/data-ooc/data-ooc.ts +204 -0
  81. package/server/service/data-ooc/index.ts +7 -0
  82. package/server/service/data-sample/data-sample-mutation.ts +6 -128
  83. package/server/service/data-sample/data-sample-type.ts +7 -28
  84. package/server/service/data-sample/data-sample.ts +33 -3
  85. package/server/service/data-set/data-set-mutation.ts +1 -4
  86. package/server/service/data-set/data-set-query.ts +87 -2
  87. package/server/service/data-set/data-set-type.ts +37 -4
  88. package/server/service/data-set/data-set.ts +52 -12
  89. package/server/service/data-spec/data-spec-manager.ts +21 -0
  90. package/server/service/data-spec/data-spec-query.ts +21 -0
  91. package/server/service/data-spec/data-spec.ts +44 -0
  92. package/server/service/data-spec/index.ts +5 -0
  93. package/server/service/index.ts +16 -8
  94. package/things-factory.config.js +14 -6
  95. package/translations/en.json +30 -0
  96. package/translations/ko.json +30 -1
  97. package/translations/ms.json +29 -0
  98. package/translations/zh.json +29 -0
  99. package/yarn-error.log +23244 -0
@@ -1,53 +1,81 @@
1
1
  import '@operato/data-grist'
2
- import './data-item-list'
3
- import './data-set-importer'
2
+ import './data-item-list.js'
3
+ import './data-set-importer.js'
4
+ import '../data-entry/data-entry-form.js'
4
5
 
5
6
  import gql from 'graphql-tag'
7
+ import JSON5 from 'json5'
6
8
  import { css, html } from 'lit'
7
9
  import moment from 'moment-timezone'
8
10
  import { connect } from 'pwa-helpers/connect-mixin'
9
11
 
12
+ import { getEditor, getRenderer } from '@operato/data-grist'
13
+ import { OxDataUseCase } from '@operato/dataset'
10
14
  import { client } from '@operato/graphql'
11
15
  import { i18next, localize } from '@operato/i18n'
12
16
  import { notify, openPopup } from '@operato/layout'
13
17
  import { PageView, store } from '@operato/shell'
14
- import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
18
+ import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
15
19
  import { isMobileDevice } from '@operato/utils'
16
20
 
17
- export class DataSet extends connect(store)(localize(i18next)(PageView)) {
21
+ const DEFAULT_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone
22
+ const TIMEZONE_OPTIONS = ['', DEFAULT_TZ, ...moment.tz.names().filter(tz => tz !== DEFAULT_TZ)]
23
+
24
+ const VIEW_TYPES = [
25
+ {
26
+ display: '',
27
+ value: ''
28
+ },
29
+ {
30
+ display: 'Generated',
31
+ value: 'generated'
32
+ },
33
+ {
34
+ display: 'Board',
35
+ value: 'board'
36
+ },
37
+ {
38
+ display: 'Page',
39
+ value: 'page'
40
+ },
41
+ {
42
+ display: 'External URL',
43
+ value: 'external'
44
+ }
45
+ ]
46
+
47
+ const USECASE_OPTIONS = () => {
48
+ return ['', ...OxDataUseCase.getUseCaseNames()].map(name => {
49
+ return {
50
+ display: name,
51
+ value: name
52
+ }
53
+ })
54
+ }
55
+
56
+ export class DataSetListPage extends connect(store)(localize(i18next)(PageView)) {
18
57
  static get properties() {
19
58
  return {
20
59
  active: String,
21
- gristConfig: Object
60
+ gristConfig: Object,
61
+ filters: Object,
62
+ sorters: Object,
63
+ mode: String
22
64
  }
23
65
  }
24
66
 
25
67
  static get styles() {
26
68
  return [
27
69
  ScrollbarStyles,
70
+ CommonGristStyles,
28
71
  css`
29
72
  :host {
30
73
  display: flex;
31
- flex-direction: column;
32
-
33
- overflow: hidden;
34
- }
35
-
36
- ox-grist {
37
- overflow-y: auto;
38
- flex: 1;
39
- }
40
-
41
- #filters {
42
- display: flex;
43
- flex-direction: row;
44
- justify-content: space-between;
45
74
 
46
- background-color: white;
47
- }
75
+ width: 100%;
48
76
 
49
- #filters > * {
50
- padding: var(--padding-default) var(--padding-wide);
77
+ --grid-record-emphasized-background-color: red;
78
+ --grid-record-emphasized-color: yellow;
51
79
  }
52
80
  `
53
81
  ]
@@ -56,7 +84,7 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
56
84
  get context() {
57
85
  return {
58
86
  title: i18next.t('title.data-set list'),
59
- help: 'integration/ui/data-set',
87
+ help: 'dataset/data-set',
60
88
  actions: [
61
89
  {
62
90
  title: i18next.t('button.copy'),
@@ -85,14 +113,43 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
85
113
  }
86
114
 
87
115
  render() {
116
+ const mode = this.mode || (isMobileDevice() ? 'LIST' : 'GRID')
117
+
88
118
  return html`
89
119
  <ox-grist
90
- .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
120
+ .mode=${mode}
91
121
  .config=${this.gristConfig}
122
+ .filters=${this.filters}
123
+ .orders=${this.orders}
92
124
  .fetchHandler=${this.fetchHandler.bind(this)}
93
125
  >
94
126
  <div slot="headroom" id="filters">
95
- <ox-filters-form></ox-filters-form>
127
+ <div id="filters">
128
+ <ox-filters-form></ox-filters-form>
129
+ </div>
130
+
131
+ <div id="sorters">
132
+ Sort
133
+ <mwc-icon
134
+ @click=${e => {
135
+ const target = e.currentTarget
136
+ this.renderRoot.querySelector('#sorter-control').open({
137
+ right: 0,
138
+ top: target.offsetTop + target.offsetHeight
139
+ })
140
+ }}
141
+ >expand_more</mwc-icon
142
+ >
143
+ <ox-popup id="sorter-control">
144
+ <ox-sorters-control> </ox-sorters-control>
145
+ </ox-popup>
146
+ </div>
147
+
148
+ <div id="modes">
149
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
150
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
151
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
152
+ </div>
96
153
  </div>
97
154
  </ox-grist>
98
155
  `
@@ -102,32 +159,12 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
102
159
  return this.renderRoot.querySelector('ox-grist')
103
160
  }
104
161
 
105
- // update with url params value
106
- _updateSearchConfig(lifecycle) {
107
- // this.searchConfig = this.searchConfig.map(conf => {
108
- // if (conf.name in lifecycle.params) {
109
- // conf.value = lifecycle.params[conf.name]
110
- // } else {
111
- // delete conf.value
112
- // }
113
- // return conf
114
- // })
115
- }
116
-
117
- // set default field value to record with searchConfig
118
- _setDefaultFieldsValue(fields) {
119
- // this.searchConfig.forEach(conf => {
120
- // if (!fields[conf.name] && conf.value) {
121
- // fields[conf.name] = conf.value
122
- // }
123
- // })
124
- }
125
-
126
162
  async pageInitialized(lifecycle) {
127
- this._updateSearchConfig(lifecycle)
128
-
129
163
  this.gristConfig = {
130
- list: { fields: ['name', 'description', 'schedule', 'active'] },
164
+ list: {
165
+ fields: ['name', 'description'],
166
+ details: ['schedule', 'active']
167
+ },
131
168
  columns: [
132
169
  { type: 'gutter', gutterName: 'sequence' },
133
170
  { type: 'gutter', gutterName: 'row-selector', multiple: true },
@@ -138,19 +175,38 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
138
175
  handlers: {
139
176
  click: (columns, data, column, record, rowIndex) => {
140
177
  if (!record.id) return
141
- openPopup(html` <data-item-list .dataSet=${record}></data-item-list> `, {
178
+ const popup = openPopup(html` <data-item-list .dataSet=${record}></data-item-list> `, {
142
179
  backdrop: true,
143
180
  help: 'data-set/ui/data-item-list',
144
181
  size: 'large',
145
182
  title: i18next.t('title.data-item list')
146
183
  })
184
+ popup.onclosed = () => {
185
+ this.grist.fetch()
186
+ }
187
+ }
188
+ }
189
+ },
190
+ {
191
+ type: 'gutter',
192
+ gutterName: 'button',
193
+ icon: 'fact_check',
194
+ handlers: {
195
+ click: (columns, data, column, record, rowIndex) => {
196
+ openPopup(
197
+ html` <data-entry-form .dataSet=${record} style="background-color: white;"></data-entry-form> `,
198
+ {
199
+ backdrop: true,
200
+ size: 'large',
201
+ title: i18next.t('title.data-entry-form')
202
+ }
203
+ )
147
204
  }
148
205
  }
149
206
  },
150
207
  {
151
208
  type: 'string',
152
209
  name: 'name',
153
- label: true,
154
210
  header: i18next.t('field.name'),
155
211
  record: {
156
212
  editable: true
@@ -162,7 +218,6 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
162
218
  {
163
219
  type: 'string',
164
220
  name: 'description',
165
- label: true,
166
221
  header: i18next.t('field.description'),
167
222
  record: {
168
223
  editable: true
@@ -178,15 +233,58 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
178
233
  record: {
179
234
  editable: true
180
235
  },
236
+ filter: true,
237
+ sortable: true,
238
+ width: 60
239
+ },
240
+ {
241
+ type: 'select',
242
+ name: 'type',
243
+ label: true,
244
+ header: i18next.t('field.type'),
245
+ record: {
246
+ editable: true,
247
+ options: [
248
+ {},
249
+ {
250
+ display: 'Manually Collected',
251
+ value: 'manual'
252
+ },
253
+ {
254
+ display: 'Automatically Collected',
255
+ value: 'automatic'
256
+ }
257
+ ]
258
+ },
181
259
  sortable: true,
260
+ filter: true,
182
261
  width: 60
183
262
  },
184
263
  {
185
- type: 'json',
264
+ type: 'select',
265
+ name: 'useCase',
266
+ label: true,
267
+ header: i18next.t('field.use-case'),
268
+ record: {
269
+ editable: true,
270
+ options: USECASE_OPTIONS
271
+ },
272
+ sortable: true,
273
+ filter: {
274
+ operator: 'eq',
275
+ options: USECASE_OPTIONS /* in case select options type is a function, filter should have its own options */
276
+ },
277
+ width: 80
278
+ },
279
+ {
280
+ type: 'partition-keys',
186
281
  name: 'partitionKeys',
187
282
  header: i18next.t('field.partition-keys'),
188
283
  record: {
189
- editable: true
284
+ editable: true,
285
+ options: {
286
+ objectified: true /* transfered as a object type */
287
+ }
190
288
  },
191
289
  width: 200
192
290
  },
@@ -196,7 +294,10 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
196
294
  label: true,
197
295
  header: i18next.t('field.schedule'),
198
296
  record: {
199
- editable: true
297
+ editable: true,
298
+ options: {
299
+ objectified: true
300
+ }
200
301
  },
201
302
  width: 80,
202
303
  label: true
@@ -207,12 +308,81 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
207
308
  header: i18next.t('field.timezone'),
208
309
  record: {
209
310
  editable: true,
210
- options: ['', ...moment.tz.names()]
311
+ options: TIMEZONE_OPTIONS
312
+ },
313
+ width: 120
314
+ },
315
+ {
316
+ type: 'resource-object',
317
+ name: 'supervisoryRole',
318
+ header: i18next.t('field.supervisory-role'),
319
+ record: {
320
+ editable: true,
321
+ options: {
322
+ queryName: 'roles'
323
+ }
211
324
  },
325
+ sortable: true,
212
326
  width: 120
213
327
  },
214
328
  {
215
- type: 'object',
329
+ type: 'select',
330
+ name: 'entryType',
331
+ label: true,
332
+ header: i18next.t('field.entry-type'),
333
+ record: {
334
+ editable: true,
335
+ options: VIEW_TYPES
336
+ },
337
+ width: 80
338
+ },
339
+ {
340
+ type: 'string',
341
+ name: 'entryView',
342
+ header: i18next.t('field.entry-view'),
343
+ record: {
344
+ editable: true,
345
+ editor: function (value, column, record, rowIndex, field) {
346
+ var type = record.entryType !== 'board' ? 'string' : 'board'
347
+ return getEditor(type)(value, column, record, rowIndex, field)
348
+ },
349
+ renderer: function (value, column, record, rowIndex, field) {
350
+ var type = record.entryType !== 'board' ? 'string' : 'board'
351
+ return getRenderer(type)(value, column, record, rowIndex, field)
352
+ }
353
+ },
354
+ width: 140
355
+ },
356
+ {
357
+ type: 'select',
358
+ name: 'monitorType',
359
+ label: true,
360
+ header: i18next.t('field.monitor-type'),
361
+ record: {
362
+ editable: true,
363
+ options: VIEW_TYPES
364
+ },
365
+ width: 80
366
+ },
367
+ {
368
+ type: 'string',
369
+ name: 'monitorView',
370
+ header: i18next.t('field.monitor-view'),
371
+ record: {
372
+ editable: true,
373
+ editor: function (value, column, record, rowIndex, field) {
374
+ var type = record.monitorType !== 'board' ? 'string' : 'board'
375
+ return getEditor(type)(value, column, record, rowIndex, field)
376
+ },
377
+ renderer: function (value, column, record, rowIndex, field) {
378
+ var type = record.monitorType !== 'board' ? 'string' : 'board'
379
+ return getRenderer(type)(value, column, record, rowIndex, field)
380
+ }
381
+ },
382
+ width: 140
383
+ },
384
+ {
385
+ type: 'resource-object',
216
386
  name: 'updater',
217
387
  header: i18next.t('field.updater'),
218
388
  record: {
@@ -251,10 +421,28 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
251
421
 
252
422
  async pageUpdated(changes, lifecycle) {
253
423
  if (this.active) {
254
- // update with url params value
255
- this._updateSearchConfig(lifecycle)
256
424
  await this.updateComplete
257
425
 
426
+ var filters = lifecycle.params?.['filters']
427
+ if (filters) {
428
+ try {
429
+ filters = JSON5.parse(filters)
430
+ this.filters = filters
431
+ } catch (e) {
432
+ console.error(`filters parameter parsing error: ${e}`)
433
+ }
434
+ }
435
+
436
+ var sorters = lifecycle.params?.['sorters']
437
+ if (sorters) {
438
+ try {
439
+ sorters = JSON5.parse(sorters)
440
+ this.sorters = sorters
441
+ } catch (e) {
442
+ console.error(`sorters parameter parsing error: ${e}`)
443
+ }
444
+ }
445
+
258
446
  this.grist.fetch()
259
447
  }
260
448
  }
@@ -269,10 +457,19 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
269
457
  name
270
458
  description
271
459
  partitionKeys
272
- slugger
273
460
  active
461
+ type
462
+ useCase
274
463
  schedule
275
464
  timezone
465
+ supervisoryRole {
466
+ id
467
+ name
468
+ }
469
+ entryType
470
+ entryView
471
+ monitorType
472
+ monitorView
276
473
  updater {
277
474
  id
278
475
  name
@@ -283,7 +480,10 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
283
480
  description
284
481
  sequence
285
482
  active
483
+ tag
286
484
  type
485
+ unit
486
+ options
287
487
  quota
288
488
  spec
289
489
  }
@@ -330,13 +530,6 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
330
530
  }
331
531
  }
332
532
 
333
- async stateChanged(state) {
334
- if (this.active && this._currentPopupName && !state.layout.viewparts[this._currentPopupName]) {
335
- this.grist.fetch()
336
- this._currentPopupName = null
337
- }
338
- }
339
-
340
533
  async _copyDataSet() {
341
534
  var selected = this.grist.selected
342
535
  if (selected.length == 0) return
@@ -355,7 +548,9 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
355
548
  }
356
549
  })
357
550
 
358
- if (!response.errors) this.grist.fetch()
551
+ if (!response.errors) {
552
+ this.grist.fetch()
553
+ }
359
554
  }
360
555
 
361
556
  async _updateDataSet() {
@@ -367,7 +562,6 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
367
562
  for (let key in dirtyFields) {
368
563
  patchField[key] = dirtyFields[key].after
369
564
  }
370
- this._setDefaultFieldsValue(patchField)
371
565
  patchField.cuFlag = patch.__dirty__
372
566
 
373
567
  return patchField
@@ -386,13 +580,25 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
386
580
  }
387
581
  })
388
582
 
389
- if (!response.errors) this.grist.fetch()
583
+ if (!response.errors) {
584
+ this.grist.fetch()
585
+ }
390
586
  }
391
587
  }
392
588
 
393
589
  async exportHandler() {
394
590
  const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
395
- const targetFieldSet = new Set(['id', 'name', 'type', 'description', 'schedule', 'timezone', 'active', 'dataItems'])
591
+ const targetFieldSet = new Set([
592
+ 'id',
593
+ 'name',
594
+ 'type',
595
+ 'description',
596
+ 'tag',
597
+ 'schedule',
598
+ 'timezone',
599
+ 'active',
600
+ 'dataItems'
601
+ ])
396
602
 
397
603
  return exportTargets.map(dataSet => {
398
604
  let tempObj = {}
@@ -408,11 +614,11 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
408
614
  openPopup(
409
615
  html`
410
616
  <data-set-importer
411
- .dataSets="${records}"
412
- @imported="${() => {
617
+ .dataSets=${records}
618
+ @imported=${() => {
413
619
  history.back()
414
620
  this.grist.fetch()
415
- }}"
621
+ }}
416
622
  ></data-set-importer>
417
623
  `,
418
624
  {
@@ -424,4 +630,4 @@ export class DataSet extends connect(store)(localize(i18next)(PageView)) {
424
630
  }
425
631
  }
426
632
 
427
- window.customElements.define('data-set-page', DataSet)
633
+ window.customElements.define('data-set-list-page', DataSetListPage)
package/client/route.js CHANGED
@@ -1,15 +1,23 @@
1
1
  export default function route(page) {
2
2
  switch (page) {
3
- case 'data-set':
4
- import('./pages/data-set')
3
+ case 'data-set-list':
4
+ import('./pages/data-set/data-set-list-page.js')
5
5
  return page
6
6
 
7
- case 'data-sensor':
8
- import('./pages/data-sensor')
7
+ case 'data-sensor-list':
8
+ import('./pages/data-sensor/data-sensor-list-page.js')
9
9
  return page
10
10
 
11
- case 'data-sample':
12
- import('./pages/data-sample')
11
+ case 'data-sample-list':
12
+ import('./pages/data-sample/data-sample-list-page.js')
13
+ return page
14
+
15
+ case 'data-ooc-list':
16
+ import('./pages/data-ooc/data-ooc-list-page.js')
17
+ return page
18
+
19
+ case 'data-entry-list':
20
+ import('./pages/data-entry/data-entry-list-page.js')
13
21
  return page
14
22
  }
15
23
  }
@@ -0,0 +1,133 @@
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.createDataSample = void 0;
7
+ const moment_timezone_1 = __importDefault(require("moment-timezone"));
8
+ const shell_1 = require("@things-factory/shell");
9
+ const data_item_1 = require("../service/data-item/data-item");
10
+ const data_ooc_1 = require("../service/data-ooc/data-ooc");
11
+ const data_sample_1 = require("../service/data-sample/data-sample");
12
+ const data_set_1 = require("../service/data-set/data-set");
13
+ const data_use_case_1 = require("./data-use-case");
14
+ const work_shift_1 = require("@things-factory/work-shift");
15
+ const debug = require('debug')('things-factory:dataset:controller/save-data-sample');
16
+ // parse variable javascript string pattern
17
+ const replaceVariables = (keys, dic) => {
18
+ for (const k in keys) {
19
+ const matches = keys[k].match(/\$\{\w*\}/g);
20
+ matches &&
21
+ matches.forEach(m => {
22
+ keys[k] = keys[k].replace(m, dic[m.slice(2, -1)]);
23
+ });
24
+ }
25
+ return keys;
26
+ };
27
+ // It is required UTC date for Partitioning File System like AWS S3 from Athena.
28
+ // ex) %YYYY, %MM, %DD
29
+ const formatDate = (keys, _moment) => {
30
+ for (const k in keys) {
31
+ const matches = keys[k].match(/%\w*/g);
32
+ matches &&
33
+ matches.forEach(m => {
34
+ keys[k] = keys[k].replace(m, _moment.format(m.substr(1)));
35
+ });
36
+ }
37
+ return keys;
38
+ };
39
+ async function createDataSample(dataSample, context) {
40
+ const { domain, user, tx } = context.state;
41
+ const dataSet = await tx.getRepository(data_set_1.DataSet).findOne({
42
+ where: { id: dataSample.dataSet.id }
43
+ });
44
+ const dataItems = await tx.getRepository(data_item_1.DataItem).find({
45
+ where: {
46
+ domain,
47
+ dataSet
48
+ },
49
+ order: {
50
+ sequence: 'DESC'
51
+ }
52
+ });
53
+ const spec = dataItems.reduce((spec, dataItem) => {
54
+ spec[dataItem.tag] = Object.assign(Object.assign({}, dataItem.spec), { name: dataItem.name, hidden: dataItem.hidden });
55
+ return spec;
56
+ }, {});
57
+ const collectedAt = dataSample.collectedAt || new Date();
58
+ // workDate ex) 2022-04-04
59
+ const { workDate, workShift } = await (0, work_shift_1.getWorkDateAndShift)(domain, collectedAt);
60
+ const dateFormat = 'YYYY-MM-DD';
61
+ // local time dataSet timezone or domain timezone or default 'UTC'
62
+ const timezone = dataSet['timezone'] || domain['timezone'] || 'UTC';
63
+ // const collectedAt = dataSample.collectedAt || new Date()
64
+ const localDateTz = (0, moment_timezone_1.default)(collectedAt).tz(timezone);
65
+ const defaultPartitionKeys = {
66
+ domain: domain.subdomain,
67
+ datasetid: dataSample.dataSet.id,
68
+ date: localDateTz.format(dateFormat),
69
+ workdate: workDate,
70
+ workshift: workShift
71
+ };
72
+ var partitionKeys = Object.assign(Object.assign({}, defaultPartitionKeys), dataSet.partitionKeys);
73
+ partitionKeys = formatDate(partitionKeys, localDateTz);
74
+ partitionKeys = replaceVariables(partitionKeys, Object.assign({}, dataSample.data));
75
+ const { ooc, oos } = data_use_case_1.DataUseCase.evaluate(dataSet, dataItems, dataSample.data) || {};
76
+ const result = await tx.getRepository(data_sample_1.DataSample).save(Object.assign(Object.assign({ name: dataSet.name, description: dataSet.description, useCase: dataSet.useCase }, dataSample), { domain,
77
+ partitionKeys,
78
+ spec,
79
+ ooc,
80
+ oos,
81
+ collectedAt,
82
+ workDate,
83
+ workShift, creator: user, updater: user }));
84
+ if (ooc || oos) {
85
+ const dataOoc = await tx.getRepository(data_ooc_1.DataOoc).save({
86
+ name: dataSet.name,
87
+ description: dataSet.description,
88
+ useCase: dataSet.useCase,
89
+ dataSet,
90
+ dataSample: result,
91
+ data: dataSample.data,
92
+ rawData: dataSample.rawData,
93
+ domain,
94
+ partitionKeys,
95
+ spec,
96
+ ooc,
97
+ oos,
98
+ history: [
99
+ {
100
+ user: {
101
+ id: user.id,
102
+ name: user.name
103
+ },
104
+ state: data_ooc_1.DataOocStatus.CREATED,
105
+ timestamp: Date.now()
106
+ }
107
+ ],
108
+ state: data_ooc_1.DataOocStatus.CREATED,
109
+ workDate,
110
+ workShift,
111
+ collectedAt,
112
+ creator: user,
113
+ updater: user
114
+ });
115
+ shell_1.pubsub.publish('data-ooc', {
116
+ dataOoc,
117
+ supervisoryRoleId: dataSet.supervisoryRoleId
118
+ });
119
+ shell_1.pubsub.publish('notification', {
120
+ notification: {
121
+ domain,
122
+ type: 'error',
123
+ title: `Data OOC occurred on '${dataSet.name}'`,
124
+ body: `Data OOC occurred on '${dataSet.name}'`,
125
+ url: (0, shell_1.getRedirectSubdomainPath)(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
126
+ timestamp: collectedAt
127
+ }
128
+ });
129
+ }
130
+ return result;
131
+ }
132
+ exports.createDataSample = createDataSample;
133
+ //# sourceMappingURL=create-data-sample.js.map