@things-factory/dataset 6.0.77 → 6.0.78

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 (42) hide show
  1. package/client/pages/data-set/data-set-list-page.ts +93 -91
  2. package/client/pages/data-summary/data-summary-list-page.ts +7 -7
  3. package/client/pages/data-summary/data-summary-search-page.ts +18 -34
  4. package/client/pages/data-summary/data-summary-view.ts +4 -4
  5. package/dist-client/pages/data-set/data-set-list-page.d.ts +1 -0
  6. package/dist-client/pages/data-set/data-set-list-page.js +88 -92
  7. package/dist-client/pages/data-set/data-set-list-page.js.map +1 -1
  8. package/dist-client/pages/data-summary/data-summary-list-page.js +7 -7
  9. package/dist-client/pages/data-summary/data-summary-list-page.js.map +1 -1
  10. package/dist-client/pages/data-summary/data-summary-search-page.js +18 -34
  11. package/dist-client/pages/data-summary/data-summary-search-page.js.map +1 -1
  12. package/dist-client/pages/data-summary/data-summary-view.js +2 -2
  13. package/dist-client/pages/data-summary/data-summary-view.js.map +1 -1
  14. package/dist-client/tsconfig.tsbuildinfo +1 -1
  15. package/dist-server/controllers/generate-data-summary.js +156 -13
  16. package/dist-server/controllers/generate-data-summary.js.map +1 -1
  17. package/dist-server/routes.js +20 -0
  18. package/dist-server/routes.js.map +1 -1
  19. package/dist-server/service/data-set/data-set-type.js +8 -0
  20. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  21. package/dist-server/service/data-set/data-set.js +22 -1
  22. package/dist-server/service/data-set/data-set.js.map +1 -1
  23. package/dist-server/service/data-summary/data-summary-mutation.js +16 -4
  24. package/dist-server/service/data-summary/data-summary-mutation.js.map +1 -1
  25. package/dist-server/service/data-summary/data-summary-type.js.map +1 -1
  26. package/dist-server/service/data-summary/data-summary.js +10 -23
  27. package/dist-server/service/data-summary/data-summary.js.map +1 -1
  28. package/dist-server/tsconfig.tsbuildinfo +1 -1
  29. package/helps/dataset/data-set.md +26 -11
  30. package/helps/dataset/ui/data-item-list.md +16 -9
  31. package/package.json +11 -11
  32. package/server/controllers/generate-data-summary.ts +202 -14
  33. package/server/routes.ts +29 -0
  34. package/server/service/data-set/data-set-type.ts +7 -1
  35. package/server/service/data-set/data-set.ts +20 -0
  36. package/server/service/data-summary/data-summary-mutation.ts +13 -4
  37. package/server/service/data-summary/data-summary-type.ts +2 -7
  38. package/server/service/data-summary/data-summary.ts +9 -24
  39. package/translations/en.json +4 -0
  40. package/translations/ko.json +5 -1
  41. package/translations/ms.json +4 -0
  42. package/translations/zh.json +4 -0
@@ -1,28 +1,43 @@
1
- # DataSet
1
+ # 데이터셋 (DataSet)
2
+
2
3
  데이타 셋을 정의합니다.<br>
3
- - [`데이타 아이템 등록 및 조회`](./ui/data-key-item-list.md): 데이타 아이템 화면을 팝업으로 띄웁니다.
4
+
5
+ - [`데이타 아이템 등록 및 조회`](./ui/data-item-list.md): 데이타 아이템 화면을 팝업으로 띄웁니다.
4
6
  - 데이타 입력: 데이타 입력 화면을 팝업으로 띄웁니다.
5
7
  - 이름: 데이터셋 이름을 설정합니다.<br>
6
8
  - 설명: 데이터셋에 대한 설명을 설정합니다.<br>
7
9
  - 유형: 데이터셋 유형을 설정합니다.<br>
8
- - 수동 수집: 사용자가 입력하는 데이터셋인 경우
9
- - 자동 수집: 센서처럼 자동으로 입력되는 데이터셋인 경우
10
+ - 수동 수집: 사용자가 입력하는 데이터셋인 경우
11
+ - 자동 수집: 센서처럼 자동으로 입력되는 데이터셋인 경우
10
12
  - 데이타 키셋: 데이타 키셋 마스터 페이지에서 등록한 항목을 적용합니다.<br>
11
13
  - 파티션 키: (deprecated 예정)<br>
12
14
  - 스케줄: 입력 주기를 설정합니다.<br>
13
- - 타임존: 수집일 타임존을 설정합니다.<br>
15
+ - 입력 주기가 설정되면, 설정된 주기마다 입력담당 역할자에게 데이타 입력 태스크가 할당됩니다.
16
+ - 타임존: 스케쥴이 반영될 기준 타임존을 설정합니다.<br>
14
17
  - 관리자 역할: 데이터셋 관리자 역할을 설정합니다.<br>
15
18
  - 입력담당 역할: 입력 권한을 부여할 역할을 설정합니다.<br>
16
19
  - 입력용 화면종류: 입력 화면종류를 설정합니다. 주로 Board가 사용됩니다.<br>
17
- - Generated: 구현된 화면을 사용합니다.
18
- - Board: Board 화면을 사용합니다.
19
- - Page: 구현된 페이지로 이동합니다. suburl이 필요합니다.
20
- - External URL: 외부 페이지로 이동합니다. 전체 Url이 필요합니다.
20
+ - Generated: 구현된 화면을 사용합니다.
21
+ - Board: Board 화면을 사용합니다.
22
+ - Page: 구현된 페이지로 이동합니다. suburl이 필요합니다.
23
+ - External URL: 외부 페이지로 이동합니다. 전체 Url이 필요합니다.
21
24
  - 입력용 화면: 화면 종류에 따른 값을 설정합니다. Board 화면 종류는 Board를 선택할 수 있습니다.<br>
22
25
  - 모니터용 화면종류: 모니터용 화면종류를 설정합니다. 선택 목록은 입력용 화면종류와 동일합니다.<br>
23
26
  - 모니터용 화면: 모니터용 화면종류에 해당하는 값을 설정합니다. <br>
24
27
  - 리포트용 화면종류: 리포트용 화면종류를 설정합니다. 선택 목록은 입력용 화면종류에 두 종류가 더 있습니다.
25
- - Jasper: Jasper 서버 설정에 따른 jasper 페이지를 렌더링합니다.
26
- - Shiny: shiny 서버 설정에 따른 shiny 페이지를 렌더링합니다.
28
+ - Jasper: Jasper 서버 설정에 따른 jasper 페이지를 렌더링합니다.
29
+ - Shiny: shiny 서버 설정에 따른 shiny 페이지를 렌더링합니다.
27
30
  - 리포트용 화면: 리포트용 화면종류에 맞는 값을 설정합니다. Jasper나 Shiny의 경우, 해당 서버에서 유효한 suburl이 필요합니다. <br>
28
31
  - 리포트용 템플릿: 리포트용 화면에 필요한 템플릿 파일을 업로드합니다.
32
+ - 서머리 주기
33
+ - 서머리 주기가 설정되면, 자동으로 스케쥴러에 서머리 작업이 등록됩니다.
34
+ - 현재는 주별, 월별 주기 서머리는 작업의 부하를 고려해서 제공하지 않습니다.
35
+ - 데이터 키셋으로 그룹핑되어서 서머리 레코드가 생성됩니다.
36
+ - 주기
37
+ - hour : 매시간 서머리 작업 진행
38
+ - shift : 매 작업조별로 진행
39
+ - workdate : 매 작업일 기준
40
+ - daily : 매일
41
+ - 다음 서머리 실행 일시
42
+ - 설정된 서머리 주기를 기준으로 다음 서머리 실행 일시를 알려줍니다.
43
+ - 서머리 주기는 설정되었지만, 스케쥴러에 등록되지 않았다면, 등록버튼이 보여지므로 버튼을 눌러서 등록합니다. 등록이 성공되면 다음 스케쥴이 표시됩니다.
@@ -1,19 +1,26 @@
1
- # DataItemList
1
+ # 데이타 아이템 등록 및 조회 (DataItemList)
2
+
2
3
  데이타 셋의 아이템 목록을 등록합니다.
4
+
3
5
  - 이름: 데이타 셋 아이템 이름입니다.
4
6
  - 설명: 데이타 셋 아아템 설명입니다.
5
7
  - 활성화: 사용 여부를 설정합니다.
6
8
  - 숨기기: 레포트 등 2차 가공 데이터로 사용시, 사용자에게 보여줄 지 여부를 설정합니다.
7
9
  - 태그이름: 오브젝트 키를 설정합니다.
8
10
  - 유형: 데이타 셋 아이템 값의 데이타 타입 입니다.
9
- - number
10
- - text
11
- - select
12
- - boolean
13
- - file
14
- - Options: select 유형인 경우, name과 value 쌍을 추가로 설정합니다.
11
+ - number
12
+ - text
13
+ - select
14
+ - boolean
15
+ - file
16
+ - 선택옵션: select 유형인 경우, name과 value 쌍을 추가로 설정합니다.
17
+ - 통계함수:
18
+ - sum, mean, stddev, variance, min, max, range, median, mode
19
+ - 주기적으로 기간별 데이터를 모아서 서머리 레코드를 생성할 때, 해당 아이템에 대해서 적용될 통계함수를 설정한다.
20
+ - 통계함수가 설정되면, 통계함수의 결과 값이 데이타 아이템 순서대로 서머리 레코드의 data{index} 컬럼에 최대 5개까지 저장된다.
15
21
  - 단위: kg, cm, 등 의 입력 단위를 설정합니다.
16
22
  - 샘플수: 해당 아이템 값을 몇 개 입력받을지 설정합니다.
17
23
  - 명세: 명세를 정의하면 이탈값이 OOC 데이터로 추가됩니다. 또한 해당 데이터샘플에 이탈여부가 기록됩니다.
18
- - CCP: Critical Control Point Data Spec을 정의합니다.
19
- - QC: Quality Contol Data Spec을 정의합니다.
24
+ - CCP: Critical Control Point Data Spec을 정의합니다.
25
+ - QC: Quality Contol Data Spec을 정의합니다.
26
+ - SPC: 아직 지원도지 않습니다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/dataset",
3
- "version": "6.0.77",
3
+ "version": "6.0.78",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -36,19 +36,19 @@
36
36
  "@operato/shell": "^1.0.1",
37
37
  "@operato/styles": "^1.0.0",
38
38
  "@operato/utils": "^1.0.1",
39
- "@things-factory/auth-base": "^6.0.76",
40
- "@things-factory/aws-base": "^6.0.76",
41
- "@things-factory/board-service": "^6.0.76",
42
- "@things-factory/env": "^6.0.76",
43
- "@things-factory/organization": "^6.0.76",
44
- "@things-factory/scheduler-client": "^6.0.76",
45
- "@things-factory/shell": "^6.0.76",
46
- "@things-factory/work-shift": "^6.0.76",
47
- "@things-factory/worklist": "^6.0.76",
39
+ "@things-factory/auth-base": "^6.0.78",
40
+ "@things-factory/aws-base": "^6.0.78",
41
+ "@things-factory/board-service": "^6.0.78",
42
+ "@things-factory/env": "^6.0.78",
43
+ "@things-factory/organization": "^6.0.78",
44
+ "@things-factory/scheduler-client": "^6.0.78",
45
+ "@things-factory/shell": "^6.0.78",
46
+ "@things-factory/work-shift": "^6.0.78",
47
+ "@things-factory/worklist": "^6.0.78",
48
48
  "cron-parser": "^4.3.0",
49
49
  "moment-timezone": "^0.5.40",
50
50
  "simple-statistics": "^7.8.3",
51
51
  "statistics": "^3.3.0"
52
52
  },
53
- "gitHead": "c2e7f3677ecf8392181a46b457854cc6412dbca2"
53
+ "gitHead": "6d1b076baae617ee1c1df25e176c21d7b1a18e61"
54
54
  }
@@ -1,11 +1,19 @@
1
1
  const statistics = require('simple-statistics')
2
2
  const deepClone = require('lodash/cloneDeep')
3
3
 
4
+ import { Between } from 'typeorm'
5
+ import moment from 'moment-timezone'
6
+
4
7
  import { Sorting, getQueryBuilderFromListParams } from '@things-factory/shell'
5
8
  import { logger } from '@things-factory/env'
9
+ import {
10
+ getDateRangeForWorkDate,
11
+ getDateRangeForWorkShift,
12
+ getLatestWorkDateAndShift
13
+ } from '@things-factory/work-shift'
6
14
 
7
15
  import { DataSample } from '../service/data-sample/data-sample'
8
- import { DataSet } from '../service/data-set/data-set'
16
+ import { DataSet, DataSetSummaryPeriodType } from '../service/data-set/data-set'
9
17
 
10
18
  import { DataSummary } from '../service/data-summary/data-summary'
11
19
  import { DataKeyItem } from '../service/data-key-set/data-key-item-type'
@@ -80,14 +88,190 @@ const fillSummaryResult = (
80
88
 
81
89
  dataSummary.summary = summary
82
90
  dataItems.slice(0, 4).forEach((dataItem, idx) => {
83
- dataSummary[`data0${idx + 1}`] = summary[dataItem.tag]
91
+ const value = Number(summary[dataItem.tag])
92
+ dataSummary[`data0${idx + 1}`] = isNaN(value) ? null : value
84
93
  })
85
94
  }
86
95
 
96
+ async function getLatestTimesForPeriod(
97
+ periodType: DataSetSummaryPeriodType,
98
+ context: ResolverContext
99
+ ): Promise<{ date?: string; period?: string; range: Date[] }> {
100
+ const { domain } = context.state
101
+ const now = moment()
102
+
103
+ if (periodType == DataSetSummaryPeriodType.Hour) {
104
+ const begin = now.clone().subtract(1, 'day').startOf('hour').toDate()
105
+ const end = now.clone().startOf('hour').toDate()
106
+
107
+ return {
108
+ date: moment(begin).tz(domain.timezone).format('YYYY-MM-DD'),
109
+ period: String(begin.getHours()),
110
+ range: [begin, end]
111
+ }
112
+ } else if (periodType == DataSetSummaryPeriodType.WorkShift) {
113
+ const { workDate, workShift, shiftRange } = await getLatestWorkDateAndShift(domain, new Date())
114
+
115
+ return { date: workDate, period: workShift, range: shiftRange }
116
+ } else if (periodType == DataSetSummaryPeriodType.WorkDate) {
117
+ const { workDate, dateRange } = await getLatestWorkDateAndShift(domain, new Date())
118
+
119
+ return { date: workDate, range: dateRange }
120
+ } else if (periodType == DataSetSummaryPeriodType.Day) {
121
+ const begin = now.clone().subtract(1, 'day').startOf('day').toDate()
122
+ const end = now.clone().startOf('day').toDate()
123
+
124
+ return {
125
+ date: moment(begin).tz(domain.timezone).format('YYYY-MM-DD'),
126
+ range: [begin, end]
127
+ }
128
+ }
129
+ }
130
+
131
+ async function getTimesForPeriod(
132
+ periodType: DataSetSummaryPeriodType,
133
+ date: string,
134
+ period: string,
135
+ context: ResolverContext
136
+ ): Promise<{ date?: string; period?: string; range: Date[] }> {
137
+ const { domain } = context.state
138
+
139
+ if (periodType == DataSetSummaryPeriodType.Hour) {
140
+ const theDate = moment.tz(`${date} ${period}:00:00`, 'YYYY-MM-DD HH:mm:ss', domain.timezone)
141
+
142
+ const begin = theDate.clone().startOf('hour').toDate()
143
+ const end = theDate.clone().add(+1, 'hour').startOf('hour').toDate()
144
+
145
+ return {
146
+ date,
147
+ period,
148
+ range: [begin, end]
149
+ }
150
+ } else if (periodType == DataSetSummaryPeriodType.WorkShift) {
151
+ const range = await getDateRangeForWorkShift(domain, date, period)
152
+
153
+ return { date, period, range }
154
+ } else if (periodType == DataSetSummaryPeriodType.WorkDate) {
155
+ const range = await getDateRangeForWorkDate(domain, date)
156
+
157
+ return { date, range }
158
+ } else if (periodType == DataSetSummaryPeriodType.Day) {
159
+ const theDate = moment.tz(`${date} 00:00:00`, 'YYYY-MM-DD HH:mm:ss', domain.timezone)
160
+
161
+ const begin = theDate.clone().startOf('day').toDate()
162
+ const end = theDate.clone().add(1, 'day').startOf('day').toDate()
163
+
164
+ return {
165
+ date: moment(begin).tz(domain.timezone).format('YYYY-MM-DD'),
166
+ range: [begin, end]
167
+ }
168
+ }
169
+ }
170
+
171
+ export async function generateLatestDataSummaries(dataSetId: string, context: ResolverContext): Promise<boolean> {
172
+ const { domain, user, tx } = context.state
173
+
174
+ try {
175
+ const dataSet = await tx.getRepository(DataSet).findOne({
176
+ where: { domain: { id: domain.id }, id: dataSetId },
177
+ relations: ['dataKeySet']
178
+ })
179
+
180
+ const dataKeyItems = dataSet.dataKeySet?.dataKeyItems || []
181
+ const dataItems = dataSet.dataItems.filter(item => item.stat)
182
+ const initialSummary = dataItems.reduce((sum, item) => {
183
+ sum[item.tag] = []
184
+ return sum
185
+ }, {})
186
+
187
+ const { date, period, range } = await getLatestTimesForPeriod(dataSet.summaryPeriod, context)
188
+ const limit = 100
189
+ var page = 1
190
+
191
+ var summaries: Partial<DataSummary>[] = []
192
+ var summary: Partial<DataSummary>
193
+
194
+ do {
195
+ const samples = await getQueryBuilderFromListParams({
196
+ repository: tx.getRepository(DataSample),
197
+ params: {
198
+ filters: [{ name: 'dataSetId', operator: 'eq', value: dataSetId }],
199
+ pagination: { page, limit },
200
+ sortings: [...buildKeySortingList(dataKeyItems), { name: 'collectedAt', desc: true }]
201
+ },
202
+ domain
203
+ })
204
+ .andWhere({ collectedAt: Between.call(range) })
205
+ .getMany()
206
+
207
+ for (const sample of samples) {
208
+ if (!summary || !compareKeys(dataKeyItems, summary, sample)) {
209
+ if (summary) {
210
+ fillSummaryResult(summary, dataItems, summary.summary)
211
+ summaries.push(summary)
212
+ }
213
+
214
+ summary = {
215
+ domain,
216
+ name: dataSet.name,
217
+ description: dataSet.description,
218
+ date,
219
+ period,
220
+ dataSet,
221
+ ...buildKeysFromSample(dataKeyItems, sample),
222
+ count: 0,
223
+ countOoc: 0,
224
+ countOos: 0,
225
+ summary: deepClone(initialSummary),
226
+ updater: user,
227
+ creator: user
228
+ }
229
+ }
230
+
231
+ summary.count++
232
+ sample.ooc && summary.countOoc++
233
+ sample.oos && summary.countOos++
234
+
235
+ dataItems.forEach(item => {
236
+ summary.summary[item.tag].push(sample.data[item.tag])
237
+ })
238
+ }
239
+
240
+ if (samples.length < limit) {
241
+ if (summary) {
242
+ fillSummaryResult(summary, dataItems, summary.summary)
243
+ summaries.push(summary)
244
+ }
245
+ break
246
+ }
247
+
248
+ page++
249
+ } while (true)
250
+
251
+ tx.getRepository(DataSummary).upsert(summaries, [
252
+ 'domain',
253
+ 'dataSet',
254
+ 'key01',
255
+ 'key02',
256
+ 'key03',
257
+ 'key04',
258
+ 'key05',
259
+ 'date',
260
+ 'period'
261
+ ])
262
+
263
+ return true
264
+ } catch (e) {
265
+ logger.error(e)
266
+ }
267
+
268
+ return false
269
+ }
270
+
87
271
  export async function generateDataSummary(
88
272
  dataSetId: string,
89
- workDate: string,
90
- workShift: string = 'NA',
273
+ date: string,
274
+ period: string,
91
275
  context: ResolverContext
92
276
  ): Promise<boolean> {
93
277
  const { domain, user, tx } = context.state
@@ -105,6 +289,10 @@ export async function generateDataSummary(
105
289
  return sum
106
290
  }, {})
107
291
 
292
+ const times = await getTimesForPeriod(dataSet.summaryPeriod, date, period, context)
293
+ const range = times.range
294
+ period = times.period
295
+
108
296
  const limit = 100
109
297
  var page = 1
110
298
 
@@ -115,16 +303,16 @@ export async function generateDataSummary(
115
303
  const samples = await getQueryBuilderFromListParams({
116
304
  repository: tx.getRepository(DataSample),
117
305
  params: {
118
- filters: [
119
- { name: 'dataSetId', operator: 'eq', value: dataSetId },
120
- { name: 'workDate', operator: 'eq', value: workDate },
121
- { name: 'workShift', operator: 'eq', value: workShift }
122
- ],
306
+ filters: [{ name: 'dataSetId', operator: 'eq', value: dataSetId }],
123
307
  pagination: { page, limit },
124
308
  sortings: [...buildKeySortingList(dataKeyItems), { name: 'collectedAt', desc: true }]
125
309
  },
126
310
  domain
127
- }).getMany()
311
+ })
312
+ .andWhere({
313
+ collectedAt: Between.apply(null, range)
314
+ })
315
+ .getMany()
128
316
 
129
317
  for (const sample of samples) {
130
318
  if (!summary || !compareKeys(dataKeyItems, summary, sample)) {
@@ -137,8 +325,8 @@ export async function generateDataSummary(
137
325
  domain,
138
326
  name: dataSet.name,
139
327
  description: dataSet.description,
140
- workDate,
141
- workShift,
328
+ date,
329
+ period,
142
330
  dataSet,
143
331
  ...buildKeysFromSample(dataKeyItems, sample),
144
332
  count: 0,
@@ -178,8 +366,8 @@ export async function generateDataSummary(
178
366
  'key03',
179
367
  'key04',
180
368
  'key05',
181
- 'workDate',
182
- 'workShift'
369
+ 'date',
370
+ 'period'
183
371
  ])
184
372
 
185
373
  return true
package/server/routes.ts CHANGED
@@ -143,6 +143,35 @@ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRout
143
143
 
144
144
  context.status = 200
145
145
  })
146
+
147
+ /* When a callback occurs from the scheduler when a scheduled summary is on schedule, data summary procedure for the dataset should be executed. */
148
+ globalPublicRouter.post('/callback-schedule-for-data-summary', async (context, next) => {
149
+ const { client } = context.request.body as ScheduleRegisterRequest
150
+
151
+ if (!client || typeof client !== 'object') {
152
+ throw new Error('client property should be a part of callback body.')
153
+ }
154
+
155
+ const { group: domainId, key: dataSetId } = client
156
+
157
+ if (!domainId || !dataSetId) {
158
+ throw new Error(`group(${domainId}) and key(${dataSetId}) properties should not be empty`)
159
+ }
160
+
161
+ await getDataSource().transaction(async tx => {
162
+ const domain = await tx.getRepository(Domain).findOneBy({ id: domainId })
163
+
164
+ if (!domain) {
165
+ throw new Error(`domain(${domainId}) not found`)
166
+ }
167
+
168
+ const dataSet = await tx.getRepository(DataSet).findOne({ where: { domain: { id: domainId }, id: dataSetId } })
169
+
170
+ // do what you gotta do
171
+ })
172
+
173
+ context.status = 200
174
+ })
146
175
  })
147
176
 
148
177
  process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
@@ -7,7 +7,7 @@ import { ApprovalLineItem } from '@things-factory/organization'
7
7
  import { AssigneeItem } from '@things-factory/worklist'
8
8
 
9
9
  import { DataItemPatch } from './data-item-type'
10
- import { DataSet, DataSetEntryType, DataSetMonitorType, DataSetReportType } from './data-set'
10
+ import { DataSet, DataSetEntryType, DataSetMonitorType, DataSetReportType, DataSetSummaryPeriodType } from './data-set'
11
11
 
12
12
  @InputType()
13
13
  export class NewDataSet {
@@ -73,6 +73,9 @@ export class NewDataSet {
73
73
 
74
74
  @Field(type => GraphQLUpload, { nullable: true })
75
75
  reportTemplate?: FileUpload
76
+
77
+ @Field({ nullable: true })
78
+ summaryPeriod?: DataSetSummaryPeriodType
76
79
  }
77
80
 
78
81
  @InputType()
@@ -143,6 +146,9 @@ export class DataSetPatch {
143
146
  @Field(type => GraphQLUpload, { nullable: true })
144
147
  reportTemplate?: FileUpload
145
148
 
149
+ @Field({ nullable: true })
150
+ summaryPeriod?: DataSetSummaryPeriodType
151
+
146
152
  @Field()
147
153
  cuFlag: string
148
154
  }
@@ -60,6 +60,18 @@ registerEnumType(DataSetReportType, {
60
60
  description: 'report type enumeration for data-set'
61
61
  })
62
62
 
63
+ export enum DataSetSummaryPeriodType {
64
+ Hour = 'hour',
65
+ WorkShift = 'work-shift',
66
+ WorkDate = 'work-date',
67
+ Day = 'day'
68
+ }
69
+
70
+ registerEnumType(DataSetSummaryPeriodType, {
71
+ name: 'DataSetSummaryPeriodType',
72
+ description: 'summary period type enumeration for data-set'
73
+ })
74
+
63
75
  @ObjectType()
64
76
  export class DataSetState {
65
77
  @Field()
@@ -199,6 +211,14 @@ export class DataSet {
199
211
  @Field({ nullable: true })
200
212
  scheduleId?: string
201
213
 
214
+ @Column({ nullable: true })
215
+ @Field({ nullable: true })
216
+ summaryPeriod?: DataSetSummaryPeriodType
217
+
218
+ @Column({ nullable: true })
219
+ @Field({ nullable: true })
220
+ summaryScheduleId?: string
221
+
202
222
  @CreateDateColumn()
203
223
  @Field({ nullable: true })
204
224
  createdAt?: Date
@@ -2,18 +2,27 @@ import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
2
2
 
3
3
  import { DataSummary } from './data-summary'
4
4
 
5
- import { generateDataSummary } from '../../controllers/generate-data-summary'
5
+ import { generateDataSummary, generateLatestDataSummaries } from '../../controllers/generate-data-summary'
6
6
 
7
7
  @Resolver(DataSummary)
8
8
  export class DataSummaryMutation {
9
+ @Directive('@transaction')
10
+ @Mutation(returns => Boolean, { description: 'To generate new Data Summaries' })
11
+ async generateLatestDataSummaries(
12
+ @Arg('dataSetId') dataSetId: string,
13
+ @Ctx() context: ResolverContext
14
+ ): Promise<boolean> {
15
+ return await generateLatestDataSummaries(dataSetId, context)
16
+ }
17
+
9
18
  @Directive('@transaction')
10
19
  @Mutation(returns => Boolean, { description: 'To generate new Data Summaries' })
11
20
  async generateDataSummaries(
12
21
  @Arg('dataSetId') dataSetId: string,
13
- @Arg('workDate') workDate: string,
14
- @Arg('workShift') workShift: string,
22
+ @Arg('date') date: string,
23
+ @Arg('period') period: string,
15
24
  @Ctx() context: ResolverContext
16
25
  ): Promise<boolean> {
17
- return await generateDataSummary(dataSetId, workDate, workShift, context)
26
+ return await generateDataSummary(dataSetId, date, period, context)
18
27
  }
19
28
  }
@@ -1,10 +1,5 @@
1
- import type { FileUpload } from 'graphql-upload/GraphQLUpload.js'
2
- import GraphQLUpload from 'graphql-upload/GraphQLUpload.js'
3
- import { ObjectType, Field, InputType, Int, ID, registerEnumType } from 'type-graphql'
4
-
5
- import { ObjectRef, ScalarObject } from '@things-factory/shell'
6
-
7
- import { DataSummary, DataSummaryDurationType } from './data-summary'
1
+ import { ObjectType, Field, InputType, Int, ID } from 'type-graphql'
2
+ import { DataSummary } from './data-summary'
8
3
 
9
4
  @InputType()
10
5
  export class NewDataSummary {
@@ -14,22 +14,7 @@ import { Domain, ScalarObject } from '@things-factory/shell'
14
14
  import { User } from '@things-factory/auth-base'
15
15
 
16
16
  import { DataItem } from '../data-set/data-item-type'
17
- import { DataSet } from '../data-set/data-set'
18
-
19
- export enum DataSummaryDurationType {
20
- Shift = 'shift',
21
- Daily = 'daily',
22
- Weekly = 'weekly',
23
- Monthly = 'monthly',
24
- Quarterly = 'quarterly',
25
- Yearly = 'yearly',
26
- Custom = 'custom'
27
- }
28
-
29
- registerEnumType(DataSummaryDurationType, {
30
- name: 'DataSummaryDurationType',
31
- description: 'entry type enumeration for data-summary'
32
- })
17
+ import { DataSet, DataSetSummaryPeriodType } from '../data-set/data-set'
33
18
 
34
19
  @Entity()
35
20
  @Index(
@@ -42,14 +27,14 @@ registerEnumType(DataSummaryDurationType, {
42
27
  dataSummary.key03,
43
28
  dataSummary.key05,
44
29
  dataSummary.key04,
45
- dataSummary.workDate,
46
- dataSummary.workShift
30
+ dataSummary.date,
31
+ dataSummary.period
47
32
  ],
48
33
  { unique: true }
49
34
  )
50
35
  @Index(
51
36
  'ix_data_summary_1',
52
- (dataSummary: DataSummary) => [dataSummary.domain, dataSummary.dataSet, dataSummary.workDate, dataSummary.workShift],
37
+ (dataSummary: DataSummary) => [dataSummary.domain, dataSummary.dataSet, dataSummary.date, dataSummary.period],
53
38
  { unique: false }
54
39
  )
55
40
  @ObjectType({ description: 'Entity for DataSummary' })
@@ -75,15 +60,15 @@ export class DataSummary {
75
60
 
76
61
  @Column({ nullable: true })
77
62
  @Field({ nullable: true })
78
- type?: DataSummaryDurationType
63
+ type?: DataSetSummaryPeriodType
79
64
 
80
- @Column({ nullable: true })
65
+ @Column({ nullable: true, default: '' })
81
66
  @Field({ nullable: true })
82
- workDate?: string
67
+ date?: string = ''
83
68
 
84
- @Column({ nullable: true, default: 'NA' })
69
+ @Column({ nullable: true, default: '' })
85
70
  @Field({ nullable: true })
86
- workShift?: string = 'NA'
71
+ period?: string = ''
87
72
 
88
73
  @ManyToOne(type => DataSet)
89
74
  @Field(type => DataSet, { nullable: false })
@@ -14,6 +14,7 @@
14
14
  "field.data-key-set": "data key set",
15
15
  "field.data-sample": "data sample",
16
16
  "field.data-set": "data set",
17
+ "field.date": "date",
17
18
  "field.device-id": "device id",
18
19
  "field.download-url": "download url",
19
20
  "field.entry-role": "entry role",
@@ -38,7 +39,10 @@
38
39
  "field.next-schedule": "next schedule",
39
40
  "field.oos": "out of critical limit",
40
41
  "field.ooc": "out of control limit",
42
+ "field.time-period": "time period",
41
43
  "field.summary": "summary",
44
+ "field.summary-period": "summary period",
45
+ "field.next-summary-schedule": "next summary schedule",
42
46
  "field.stat-function": "stat func.",
43
47
  "field.count": "count",
44
48
  "field.count-oos": "oos count",
@@ -14,6 +14,7 @@
14
14
  "field.data": "데이타",
15
15
  "field.data-key": "데이타 키",
16
16
  "field.data-key-set": "데이타 키셋",
17
+ "field.date": "일자",
17
18
  "field.device-id": "디바이스 아이디",
18
19
  "field.download-url": "다운로드 주소",
19
20
  "field.entry-role": "입력담당 역할",
@@ -38,7 +39,10 @@
38
39
  "field.next-schedule": "다음 수집계획",
39
40
  "field.ooc": "관리한계 이탈여부",
40
41
  "field.oos": "허용한계 이탈여부",
41
- "field.summary": "마감 자료",
42
+ "field.time-period": "시간단위",
43
+ "field.summary": "서머리",
44
+ "field.summary-period": "서머리 주기",
45
+ "field.next-summary-schedule": "다음 서머리 스케쥴",
42
46
  "field.stat-function": "통계함수",
43
47
  "field.count": "건수",
44
48
  "field.count-oos": "허용한계이탈 건수",
@@ -14,6 +14,7 @@
14
14
  "field.data-key-set": "data key set",
15
15
  "field.data-sample": "data sample",
16
16
  "field.data-set": "data set",
17
+ "field.date": "date",
17
18
  "field.device-id": "device id",
18
19
  "field.entry-role": "entry role",
19
20
  "field.entry-type": "entry type",
@@ -37,7 +38,10 @@
37
38
  "field.next-schedule": "next schedule",
38
39
  "field.oos": "out of critical limit",
39
40
  "field.ooc": "out of control limit",
41
+ "field.time-period": "time period",
40
42
  "field.summary": "summary",
43
+ "field.summary-period": "summary period",
44
+ "field.next-summary-schedule": "next summary schedule",
41
45
  "field.stat-function": "stat func.",
42
46
  "field.count": "count",
43
47
  "field.count-oos": "oos count",