@things-factory/dataset 6.0.27 → 6.0.28

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 (90) hide show
  1. package/assets/images/data-collect.png +0 -0
  2. package/client/bootstrap.ts +4 -2
  3. package/client/components/data-collect-activity-view.ts +100 -0
  4. package/client/components/ooc-resolve-activity-view.ts +174 -0
  5. package/client/components/{data-ooc-activity-view.ts → ooc-review-activity-view.ts} +16 -16
  6. package/client/pages/data-ooc/data-ooc-list-page.ts +6 -0
  7. package/client/pages/data-ooc/data-ooc-view-page.ts +115 -0
  8. package/client/pages/data-ooc/data-ooc-view.ts +7 -61
  9. package/client/pages/data-set/data-set-list-page.ts +111 -4
  10. package/client/route.ts +4 -0
  11. package/config/config.development.js +30 -30
  12. package/config/config.production.js +31 -31
  13. package/dist-client/bootstrap.d.ts +3 -1
  14. package/dist-client/bootstrap.js +4 -2
  15. package/dist-client/bootstrap.js.map +1 -1
  16. package/dist-client/components/data-collect-activity-view.d.ts +19 -0
  17. package/dist-client/components/data-collect-activity-view.js +101 -0
  18. package/dist-client/components/data-collect-activity-view.js.map +1 -0
  19. package/dist-client/components/ooc-resolve-activity-view.d.ts +1 -0
  20. package/dist-client/components/ooc-resolve-activity-view.js +175 -0
  21. package/dist-client/components/ooc-resolve-activity-view.js.map +1 -0
  22. package/dist-client/components/ooc-review-activity-view.d.ts +1 -0
  23. package/dist-client/components/ooc-review-activity-view.js +155 -0
  24. package/dist-client/components/ooc-review-activity-view.js.map +1 -0
  25. package/dist-client/pages/data-ooc/data-ooc-list-page.js +6 -0
  26. package/dist-client/pages/data-ooc/data-ooc-list-page.js.map +1 -1
  27. package/dist-client/pages/data-ooc/data-ooc-view-page.d.ts +18 -0
  28. package/dist-client/pages/data-ooc/data-ooc-view-page.js +114 -0
  29. package/dist-client/pages/data-ooc/data-ooc-view-page.js.map +1 -0
  30. package/dist-client/pages/data-ooc/data-ooc-view.d.ts +15 -1
  31. package/dist-client/pages/data-ooc/data-ooc-view.js +9 -58
  32. package/dist-client/pages/data-ooc/data-ooc-view.js.map +1 -1
  33. package/dist-client/pages/data-set/data-set-list-page.d.ts +2 -0
  34. package/dist-client/pages/data-set/data-set-list-page.js +106 -4
  35. package/dist-client/pages/data-set/data-set-list-page.js.map +1 -1
  36. package/dist-client/route.js +3 -0
  37. package/dist-client/route.js.map +1 -1
  38. package/dist-client/tsconfig.tsbuildinfo +1 -1
  39. package/dist-server/controllers/activity-template/activity-data-collect.js +82 -0
  40. package/dist-server/controllers/activity-template/activity-data-collect.js.map +1 -0
  41. package/dist-server/controllers/activity-template/activity-ooc-resolve.js +100 -0
  42. package/dist-server/controllers/activity-template/activity-ooc-resolve.js.map +1 -0
  43. package/dist-server/controllers/activity-template/activity-ooc-review.js +107 -0
  44. package/dist-server/controllers/activity-template/activity-ooc-review.js.map +1 -0
  45. package/dist-server/controllers/create-data-sample.js +44 -42
  46. package/dist-server/controllers/create-data-sample.js.map +1 -1
  47. package/dist-server/controllers/data-use-case.js +1 -1
  48. package/dist-server/controllers/data-use-case.js.map +1 -1
  49. package/dist-server/controllers/index.js +4 -2
  50. package/dist-server/controllers/index.js.map +1 -1
  51. package/dist-server/middlewares/index.js +8 -0
  52. package/dist-server/middlewares/index.js.map +1 -0
  53. package/dist-server/migrations/index.js +12 -0
  54. package/dist-server/migrations/index.js.map +1 -0
  55. package/dist-server/routes.js +45 -1
  56. package/dist-server/routes.js.map +1 -1
  57. package/dist-server/service/data-ooc/data-ooc-query.js +22 -2
  58. package/dist-server/service/data-ooc/data-ooc-query.js.map +1 -1
  59. package/dist-server/service/data-ooc/data-ooc.js +26 -0
  60. package/dist-server/service/data-ooc/data-ooc.js.map +1 -1
  61. package/dist-server/service/data-set/data-set-mutation.js +88 -0
  62. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  63. package/dist-server/service/data-set/data-set-query.js +33 -0
  64. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  65. package/dist-server/service/data-set/data-set-type.js +16 -0
  66. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  67. package/dist-server/service/data-set/data-set.js +52 -3
  68. package/dist-server/service/data-set/data-set.js.map +1 -1
  69. package/dist-server/tsconfig.tsbuildinfo +1 -1
  70. package/helps/dataset/data-ooc.md +5 -4
  71. package/package.json +10 -8
  72. package/server/controllers/activity-template/activity-data-collect.ts +84 -0
  73. package/server/controllers/activity-template/activity-ooc-resolve.ts +114 -0
  74. package/server/controllers/activity-template/activity-ooc-review.ts +118 -0
  75. package/server/controllers/create-data-sample.ts +48 -53
  76. package/server/controllers/data-use-case.ts +1 -1
  77. package/server/controllers/index.ts +4 -2
  78. package/server/routes.ts +61 -2
  79. package/server/service/data-ooc/data-ooc-query.ts +12 -2
  80. package/server/service/data-ooc/data-ooc.ts +23 -0
  81. package/server/service/data-set/data-set-mutation.ts +99 -0
  82. package/server/service/data-set/data-set-query.ts +34 -1
  83. package/server/service/data-set/data-set-type.ts +14 -0
  84. package/server/service/data-set/data-set.ts +40 -2
  85. package/things-factory.config.js +4 -0
  86. package/translations/en.json +2 -0
  87. package/translations/ko.json +2 -0
  88. package/translations/ms.json +2 -0
  89. package/translations/zh.json +2 -0
  90. package/server/controllers/activity-template/activity-ooc.ts +0 -55
@@ -1,10 +1,11 @@
1
1
  # DataOOC
2
- 데이타 이탈 내역을 조회하고 조치합니다.
2
+
3
+ 데이타 이탈 내역을 조회하고 조치합니다.
3
4
 
4
5
  - 데이타 이탈점 상세: 데이타 이탈점 상세 팝업 화면을 띄웁니다.
5
- - 데이타 셋 아이템 별 CCP, QC 이탈 여부를 조회합니다.
6
- - 데이타 이탈점 생성 부터 조치 이력을 조회합니다.
7
- - 조치, 조치완료 내용을 입력하고 저장합니다.
6
+ - 데이타 셋 아이템 별 CCP, QC 이탈 여부를 조회합니다.
7
+ - 데이타 이탈점 생성 부터 조치 이력을 조회합니다.
8
+ - 조치, 조치완료 내용을 입력하고 저장합니다.
8
9
  - 상태: 생성, 조치, 조치완료 상태를 보여줍니다.
9
10
  - 조치 활동: 조치 내용을 보여줍니다.
10
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/dataset",
3
- "version": "6.0.27",
3
+ "version": "6.0.28",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -35,15 +35,17 @@
35
35
  "@operato/shell": "^1.0.1",
36
36
  "@operato/styles": "^1.0.0",
37
37
  "@operato/utils": "^1.0.1",
38
- "@things-factory/auth-base": "^6.0.27",
39
- "@things-factory/aws-base": "^6.0.27",
40
- "@things-factory/board-service": "^6.0.27",
38
+ "@things-factory/auth-base": "^6.0.28",
39
+ "@things-factory/aws-base": "^6.0.28",
40
+ "@things-factory/board-service": "^6.0.28",
41
41
  "@things-factory/env": "^6.0.7",
42
- "@things-factory/shell": "^6.0.27",
43
- "@things-factory/work-shift": "^6.0.27",
44
- "@things-factory/worklist": "^6.0.27",
42
+ "@things-factory/organization": "^6.0.28",
43
+ "@things-factory/scheduler-client": "^6.0.28",
44
+ "@things-factory/shell": "^6.0.28",
45
+ "@things-factory/work-shift": "^6.0.28",
46
+ "@things-factory/worklist": "^6.0.28",
45
47
  "cron-parser": "^4.3.0",
46
48
  "moment-timezone": "^0.5.40"
47
49
  },
48
- "gitHead": "684784a4f3e450e9ce07dcd51db70b8a2d2c2a0f"
50
+ "gitHead": "8b4e03229444e38fc3f3487227a96837eef85099"
49
51
  }
@@ -0,0 +1,84 @@
1
+ import { Activity, ActivityInstance, ActivityInstanceStatus } from '@things-factory/worklist'
2
+ import { NewDataSample } from 'service/data-sample/data-sample-type'
3
+ import { createDataSample } from '../../controllers/create-data-sample'
4
+
5
+ async function callback(activityInstance: ActivityInstance, context: ResolverContext) {
6
+ const { domain, tx } = context.state
7
+ const { input, output, state } = activityInstance
8
+
9
+ if (state == ActivityInstanceStatus.Ended) {
10
+ const activity = (await tx.getRepository(Activity).findOneBy({
11
+ domain: { id: domain.id },
12
+ name: 'Collect Data'
13
+ })) as Activity
14
+
15
+ /* collect-data 액티비티는 한명의 assignee(thread)만 수행하도록 한다. 따라서, output 중에서 샘플 한개만을 data로 취한다. */
16
+ const assignees = Object.keys(output)
17
+ const data = assignees.length > 0 && output[assignees[0]]
18
+
19
+ if (activity) {
20
+ const dataSample: NewDataSample = {
21
+ dataSet: {
22
+ id: input.dataSetId
23
+ },
24
+ data
25
+ }
26
+
27
+ await createDataSample(dataSample, context)
28
+ } else {
29
+ console.error('Collect Data Activity not installed.')
30
+ }
31
+ }
32
+ }
33
+
34
+ export const ActivityDataCollect = {
35
+ name: 'Collect Data',
36
+ description: 'Tasks assigned for data collection',
37
+ release: '1.0.0',
38
+ provider: 'hatiolab.com',
39
+ category: 'quality',
40
+ activityType: 'user',
41
+ searchKeys: [
42
+ {
43
+ name: 'dataSetName',
44
+ description: 'DataSet name',
45
+ inputKey: 'dataSetName',
46
+ tKey: 'data-set-name'
47
+ }
48
+ ],
49
+ startable: true,
50
+ model: [
51
+ {
52
+ name: 'dataSetId',
53
+ description: 'DataSet Id.',
54
+ tag: 'dataSetId',
55
+ hidden: true,
56
+ mandatory: true,
57
+ inout: 'in',
58
+ type: 'string',
59
+ options: {},
60
+ unit: null,
61
+ quantifier: [1],
62
+ spec: {}
63
+ },
64
+ {
65
+ name: 'dataSetName',
66
+ description: 'DataSet name',
67
+ tag: 'dataSetName',
68
+ hidden: true,
69
+ mandatory: true,
70
+ inout: 'in',
71
+ type: 'string',
72
+ options: {},
73
+ unit: null,
74
+ quantifier: [1],
75
+ spec: {}
76
+ }
77
+ ],
78
+ uiType: 'custom-element',
79
+ uiSource: 'data-collect-activity-view',
80
+ reportType: 'page',
81
+ reportSource: 'data-collect-activity-view',
82
+ thumbnail: '/assets/images/data-collect.png',
83
+ callback /* Called when there is a change in the lifecycle of a task (activity-instance). */
84
+ }
@@ -0,0 +1,114 @@
1
+ import {
2
+ ActivityInstance,
3
+ ActivityInstanceStatus,
4
+ ActivityThread,
5
+ ActivityThreadStatus
6
+ } from '@things-factory/worklist'
7
+ import { DataOoc, DataOocStatus } from '../../service/data-ooc/data-ooc'
8
+
9
+ async function callback(activityInstance: ActivityInstance, context: ResolverContext) {
10
+ const { tx, user, domain } = context.state
11
+ const { id, input, output, state, terminatedAt } = activityInstance
12
+
13
+ /* Resolve Activity가 완료되면, 해당 data-ooc 엔티티의 correctiveAction, correctorId, correctedAt 을 update한다. */
14
+ if (state == ActivityInstanceStatus.Ended) {
15
+ const activityThreads = await tx.getRepository(ActivityThread).find({
16
+ where: {
17
+ domain: { id: domain.id },
18
+ activityInstance: { id },
19
+ state: ActivityThreadStatus.Ended
20
+ },
21
+ relations: ['assignee']
22
+ })
23
+
24
+ const corrector = activityThreads[0]?.assignee
25
+
26
+ const { dataOocId } = input
27
+ const correctiveAction = Object.values(output)
28
+ .map((o: any) => o.action)
29
+ .join('\n')
30
+
31
+ var dataOoc = await tx.getRepository(DataOoc).findOne({
32
+ where: {
33
+ domain: { id: domain.id },
34
+ id: dataOocId
35
+ },
36
+ relations: ['dataSet']
37
+ })
38
+
39
+ /* dataOoc Review 결과를 dataOoc 엔티티에 반영한다. */
40
+ dataOoc = await tx.getRepository(DataOoc).save({
41
+ ...dataOoc,
42
+ correctedAt: terminatedAt,
43
+ corrector: corrector,
44
+ correctiveAction,
45
+ updater: user,
46
+ state: DataOocStatus.CORRECTED
47
+ })
48
+ }
49
+ }
50
+
51
+ export const ActivityOocResolve = {
52
+ name: 'OOC Resolve',
53
+ description: 'Task to take remedial action based on Out Of Control data',
54
+ release: '1.0.0',
55
+ provider: 'hatiolab.com',
56
+ category: 'quality',
57
+ activityType: 'user',
58
+ searchKeys: [
59
+ {
60
+ name: 'dataOocId',
61
+ description: 'OOC Id.',
62
+ inputKey: 'dataOocId',
63
+ tKey: 'data-ooc-id'
64
+ }
65
+ ],
66
+ startable: true,
67
+ model: [
68
+ {
69
+ name: 'dataOocId',
70
+ description: 'OOC id',
71
+ tag: 'dataOocId',
72
+ hidden: true,
73
+ mandatory: true,
74
+ inout: 'in',
75
+ type: 'string',
76
+ options: {},
77
+ unit: null,
78
+ quantifier: [1],
79
+ spec: {}
80
+ },
81
+ {
82
+ name: 'instruction',
83
+ description: 'Instructions for resolving OOC',
84
+ tag: 'instruction',
85
+ hidden: false,
86
+ mandatory: true,
87
+ inout: 'in',
88
+ type: 'textarea',
89
+ options: {},
90
+ unit: null,
91
+ quantifier: [1],
92
+ spec: {}
93
+ },
94
+ {
95
+ name: 'action',
96
+ description: 'Action Description',
97
+ tag: 'action',
98
+ hidden: false,
99
+ mandatory: true,
100
+ inout: 'out',
101
+ type: 'textarea',
102
+ options: {},
103
+ unit: null,
104
+ quantifier: [1],
105
+ spec: {}
106
+ }
107
+ ],
108
+ uiType: 'custom-element',
109
+ uiSource: 'ooc-resolve-activity-view',
110
+ reportType: 'page',
111
+ reportSource: 'data-ooc-report-page',
112
+ thumbnail: '/assets/images/ooc.png',
113
+ callback /* Called when there is a change in the lifecycle of a task (activity-instance). */
114
+ }
@@ -0,0 +1,118 @@
1
+ import { Activity, ActivityInstance, ActivityInstanceStatus } from '@things-factory/worklist'
2
+ import { post } from '@things-factory/worklist/dist-server/controllers/activity-instance/post'
3
+ import { DataOoc, DataOocStatus } from '../../service/data-ooc/data-ooc'
4
+
5
+ async function callback(activityInstance: ActivityInstance, context: ResolverContext) {
6
+ const { domain, user, tx } = context.state
7
+ const { input, output, state, terminatedAt } = activityInstance
8
+
9
+ if (state == ActivityInstanceStatus.Ended) {
10
+ const activity = (await tx.getRepository(Activity).findOneBy({
11
+ domain: { id: domain.id },
12
+ name: 'OOC Resolve'
13
+ })) as Activity
14
+
15
+ if (activity) {
16
+ const { dataOocId } = input
17
+ const correctiveInstruction = Object.values(output)
18
+ .map((o: any) => o.instruction)
19
+ .join('\n')
20
+
21
+ var dataOoc = await tx.getRepository(DataOoc).findOne({
22
+ where: {
23
+ domain: { id: domain.id },
24
+ id: dataOocId
25
+ },
26
+ relations: ['dataSet']
27
+ })
28
+
29
+ const dataSet = dataOoc.dataSet
30
+ const { assignees, approvalLine } = dataSet
31
+
32
+ /* dataOoc Review 결과를 dataOoc 엔티티에 반영한다. */
33
+ dataOoc = await tx.getRepository(DataOoc).save({
34
+ ...dataOoc,
35
+ reviewedAt: terminatedAt,
36
+ reviewer: user,
37
+ correctiveInstruction,
38
+ state: DataOocStatus.REVIEWED
39
+ })
40
+
41
+ /* 해당 dataset의 작업 담당자(assignees)에게 OOC 해결을 위한 태스크를 지시한다. */
42
+ if (assignees && assignees instanceof Array && assignees.length > 0) {
43
+ const activityInstance = {
44
+ name: `[OOC 조치] ${dataSet.name}`,
45
+ activityId: activity.id,
46
+ description: `Data OOC occurred on '${dataSet.name}'`,
47
+ dueAt: new Date(terminatedAt.getTime() + 24 * 60 * 60 * 1000),
48
+ input: {
49
+ dataOocId,
50
+ instruction: correctiveInstruction
51
+ },
52
+ assignees,
53
+ approvalLine
54
+ }
55
+
56
+ await post(activityInstance, context)
57
+ } else {
58
+ console.error(
59
+ `Assignees are not set. So, Data OOC Resolve task for ${dataOoc.name}(${dataOoc.id}) could not be posted.`
60
+ )
61
+ }
62
+ } else {
63
+ console.error('OOC Resolve Activity not installed.')
64
+ }
65
+ }
66
+ }
67
+
68
+ export const ActivityOocReview = {
69
+ name: 'OOC Review',
70
+ description: 'Task to take remedial action based on Out Of Control data',
71
+ release: '1.0.0',
72
+ provider: 'hatiolab.com',
73
+ category: 'quality',
74
+ activityType: 'user',
75
+ searchKeys: [
76
+ {
77
+ name: 'dataOocId',
78
+ description: 'OOC Id.',
79
+ inputKey: 'dataOocId',
80
+ tKey: 'data-ooc-id'
81
+ }
82
+ ],
83
+ startable: true,
84
+ model: [
85
+ {
86
+ name: 'dataOocId',
87
+ description: 'OOC id',
88
+ tag: 'dataOocId',
89
+ hidden: true,
90
+ mandatory: true,
91
+ inout: 'in',
92
+ type: 'string',
93
+ options: {},
94
+ unit: null,
95
+ quantifier: [1],
96
+ spec: {}
97
+ },
98
+ {
99
+ name: 'Instruction',
100
+ description: 'Instructions for preventing OOC',
101
+ tag: 'instruction',
102
+ hidden: false,
103
+ mandatory: true,
104
+ inout: 'out',
105
+ type: 'textarea',
106
+ options: {},
107
+ unit: null,
108
+ quantifier: [1],
109
+ spec: {}
110
+ }
111
+ ],
112
+ uiType: 'custom-element',
113
+ uiSource: 'ooc-review-activity-view',
114
+ reportType: 'page',
115
+ reportSource: 'data-ooc-report-page',
116
+ thumbnail: '/assets/images/ooc.png',
117
+ callback /* Called when there is a change in the lifecycle of a task (activity-instance). */
118
+ }
@@ -1,8 +1,8 @@
1
1
  import moment from 'moment-timezone'
2
- import { EntityManager } from 'typeorm'
3
2
 
4
- import { User, Role } from '@things-factory/auth-base'
5
- import { Domain, getRedirectSubdomainPath, getRepository, pubsub } from '@things-factory/shell'
3
+ import { Role } from '@things-factory/auth-base'
4
+ import { getRedirectSubdomainPath, pubsub } from '@things-factory/shell'
5
+ import { logger } from '@things-factory/env'
6
6
  import { getWorkDateAndShift } from '@things-factory/work-shift'
7
7
 
8
8
  import { DataOoc, DataOocStatus } from '../service/data-ooc/data-ooc'
@@ -12,7 +12,7 @@ import { DataSet } from '../service/data-set/data-set'
12
12
  import { DataUseCase } from './data-use-case'
13
13
  import { Activity } from '@things-factory/worklist'
14
14
 
15
- import { draft } from '@things-factory/worklist/dist-server/controllers/activity-instance/draft'
15
+ import { post } from '@things-factory/worklist/dist-server/controllers/activity-instance/post'
16
16
 
17
17
  // See README.md at ## Data Samples
18
18
  process.env.TZ = 'UTC'
@@ -53,10 +53,7 @@ const formatDate = (keys, _moment) => {
53
53
  return keys
54
54
  }
55
55
 
56
- export async function createDataSample(
57
- dataSample: NewDataSample,
58
- context: { state: { domain: Domain; user: User; tx: EntityManager } }
59
- ): Promise<DataSample> {
56
+ export async function createDataSample(dataSample: NewDataSample, context: ResolverContext): Promise<DataSample> {
60
57
  const { domain, user, tx } = context.state
61
58
 
62
59
  const dataSet = await tx.getRepository(DataSet).findOne({
@@ -125,17 +122,12 @@ export async function createDataSample(
125
122
  name: user.name
126
123
  },
127
124
  state: DataOocStatus.CREATED,
128
- timestamp: Date.now()
125
+ timestamp: collectedAt
129
126
  }
130
127
  ],
131
128
  state: DataOocStatus.CREATED
132
129
  })
133
130
 
134
- // 여기쯤에서, worklist로 TODO로 보낸다.
135
- // 입력담당역할에게 assign한다.
136
- // 결재라인은 시설관리자을 포함한다. 결재라인을 dataset에 추가한다.
137
- // Dataset용 Installable Activity 를 만든다.
138
-
139
131
  const activity = (await tx.getRepository(Activity).findOneBy({
140
132
  domain: { id: domain.id },
141
133
  name: 'OOC Review'
@@ -143,51 +135,54 @@ export async function createDataSample(
143
135
 
144
136
  if (activity) {
145
137
  const assignee =
146
- dataSet.entryRoleId &&
147
- (await tx.getRepository(Role)).findOneBy({ domain: { id: domain.id }, id: dataSet.entryRoleId })
148
-
149
- const approver =
150
138
  dataSet.supervisoryRoleId &&
151
- (await tx.getRepository(Role)).findOneBy({ domain: { id: domain.id }, id: dataSet.supervisoryRoleId })
152
-
153
- const assignees = dataSet.entryRoleId ? [{ type: 'Role', value: dataSet.entryRoleId, assignee }] : []
154
- const approvalLine = dataSet.supervisoryRoleId
155
- ? [{ type: 'Role', value: dataSet.supervisoryRoleId, approver }]
156
- : []
157
-
158
- const activityInstance = {
159
- name: `[OOC] ${dataSet.name}`,
160
- activityId: activity.id,
161
- description: `Data OOC occurred on '${dataSet.name}'`,
162
- dueAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
163
- input: {
164
- dataOocId: dataOoc.id
165
- },
166
- assignees,
167
- approvalLine
168
- }
139
+ (await tx.getRepository(Role).findOneBy({ domain: { id: domain.id }, id: dataSet.supervisoryRoleId }))
140
+
141
+ const assignees = dataSet.supervisoryRoleId ? [{ type: 'Role', value: dataSet.supervisoryRoleId, assignee }] : []
142
+
143
+ /* 해당 dataset의 supervisor로 하여금, OOC를 리뷰하고 instruction을 작성해서, OOC 해결을 위한 태스크를 dataset assignees에게 지시하도록 한다. */
144
+ if (assignees && assignees instanceof Array && assignees.length > 0) {
145
+ const activityInstance = {
146
+ name: `[OOC 검토] ${dataSet.name}`,
147
+ activityId: activity.id,
148
+ description: `Data OOC occurred on '${dataSet.name}'`,
149
+ dueAt: new Date(collectedAt.getTime() + 24 * 60 * 60 * 1000),
150
+ input: {
151
+ dataOocId: dataOoc.id
152
+ },
153
+ assignees,
154
+ approvalLine: []
155
+ }
169
156
 
170
- draft(activityInstance, context)
157
+ await post(activityInstance, context)
158
+ } else {
159
+ console.error(
160
+ `Assignees are not set. So Data OOC Review task for ${dataOoc.name}(${dataOoc.id}) could not be posted.`
161
+ )
162
+ }
171
163
  } else {
172
- console.warn('OOC Review Activity not installed.')
164
+ console.error('OOC Review Activity not installed.')
173
165
  }
174
- //
175
166
 
176
- pubsub.publish('data-ooc', {
177
- dataOoc,
178
- supervisoryRoleId: dataSet.supervisoryRoleId
179
- })
167
+ try {
168
+ pubsub.publish('data-ooc', {
169
+ dataOoc,
170
+ supervisoryRoleId: dataSet.supervisoryRoleId
171
+ })
180
172
 
181
- pubsub.publish('notification', {
182
- notification: {
183
- domain,
184
- type: 'error',
185
- title: `[OOC] ${dataSet.name}`,
186
- body: `Data OOC occurred on '${dataSet.name}'`,
187
- url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
188
- timestamp: collectedAt
189
- }
190
- })
173
+ pubsub.publish('notification', {
174
+ notification: {
175
+ domain,
176
+ type: 'error',
177
+ title: `[OOC] ${dataSet.name}`,
178
+ body: `Data OOC occurred on '${dataSet.name}'`,
179
+ url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
180
+ timestamp: collectedAt
181
+ }
182
+ })
183
+ } catch (err) {
184
+ logger.error('Notification', err)
185
+ }
191
186
  }
192
187
 
193
188
  return result
@@ -55,7 +55,7 @@ export abstract class DataUseCase {
55
55
  continue
56
56
  }
57
57
 
58
- let values: any | any[] = data[tag]
58
+ let values: any | any[] = data && data[tag]
59
59
  if (values == null) {
60
60
  continue // TODO what if in case no value ?
61
61
  }
@@ -1,9 +1,11 @@
1
1
  import { ActivityInstallations } from '@things-factory/worklist'
2
2
 
3
- import { ActivityOoc } from './activity-template/activity-ooc'
3
+ import { ActivityOocReview } from './activity-template/activity-ooc-review'
4
+ import { ActivityOocResolve } from './activity-template/activity-ooc-resolve'
5
+ import { ActivityDataCollect } from './activity-template/activity-data-collect'
4
6
 
5
7
  /* activity templates installation */
6
- ;[ActivityOoc].forEach(template => {
8
+ ;[ActivityOocReview, ActivityOocResolve, ActivityDataCollect].forEach(template => {
7
9
  ActivityInstallations.installActivityTemplate(template)
8
10
  })
9
11
 
package/server/routes.ts CHANGED
@@ -1,10 +1,13 @@
1
- import { getDataSource } from '@things-factory/shell'
1
+ import { Domain, getDataSource } from '@things-factory/shell'
2
2
  import { User } from '@things-factory/auth-base'
3
+ import { Activity } from '@things-factory/worklist'
4
+ import { post } from '@things-factory/worklist/dist-server/controllers/activity-instance/post'
3
5
 
4
6
  import { createDataSample } from './controllers/create-data-sample'
5
7
  import { renderJasperReport } from './controllers/jasper-report'
6
8
  import { renderShinyReport } from './controllers/shiny-report'
7
9
  import { DataSensor } from './service/data-sensor/data-sensor'
10
+ import { DataSet } from './service/data-set/data-set'
8
11
 
9
12
  process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
10
13
  /*
@@ -54,6 +57,12 @@ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRout
54
57
  }
55
58
  })
56
59
 
60
+ context.state = {
61
+ ...context.state,
62
+ domain,
63
+ tx
64
+ }
65
+
57
66
  return await createDataSample(
58
67
  {
59
68
  dataSet,
@@ -62,12 +71,62 @@ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRout
62
71
  source: deviceId,
63
72
  collectedAt: new Date(timestamp)
64
73
  },
65
- { state: { domain, user, tx } }
74
+ context
66
75
  )
67
76
  })
68
77
 
69
78
  context.status = 200
70
79
  })
80
+
81
+ /* When a callback occurs from the scheduler when a scheduled dataset is on schedule, data collection task for the dataset should be issued. */
82
+ globalPublicRouter.post('/callback-schedule-for-dataset', async (context, next) => {
83
+ const { domainId, dataSetId } = context.request.body
84
+
85
+ getDataSource().transaction(async tx => {
86
+ const domain = await tx.getRepository(Domain).findOneBy({ id: domainId })
87
+ const dataSet = await tx.getRepository(DataSet).findOne({ where: { domain: { id: domainId }, id: dataSetId } })
88
+
89
+ const activity = (await tx.getRepository(Activity).findOneBy({
90
+ domain: { id: domainId },
91
+ name: 'Collect Data'
92
+ })) as Activity
93
+
94
+ if (activity) {
95
+ const { assignees } = dataSet
96
+
97
+ /* 해당 dataset에 대한 데이타 수집 태스크를 dataset assignees에게 할당한다. */
98
+ if (assignees && assignees instanceof Array && assignees.length > 0) {
99
+ const activityInstance = {
100
+ name: `[Data 수집] ${dataSet.name}`,
101
+ activityId: activity.id,
102
+ description: `Data Collect for '${dataSet.name}'`,
103
+ dueAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
104
+ input: {
105
+ dataSetId: dataSet.id,
106
+ dataSetName: dataSet.name
107
+ },
108
+ assignees
109
+ }
110
+
111
+ context.state = {
112
+ ...context.state,
113
+ domain,
114
+ tx
115
+ }
116
+
117
+ await post(activityInstance, context)
118
+ } else {
119
+ console.error(
120
+ `Assignees not set. So Data Collec Activity for ${dataSet.name}($dataSet.id) could not be posted.`
121
+ )
122
+ }
123
+ } else {
124
+ console.error(`Data Collec Activity is not installed.`)
125
+ }
126
+ })
127
+
128
+ context.status = 200
129
+ })
71
130
  })
72
131
 
73
132
  process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
@@ -56,6 +56,16 @@ export class DataOocQuery {
56
56
  })
57
57
  }
58
58
 
59
+ @FieldResolver(type => User)
60
+ async corrector(@Root() dataOoc: DataOoc): Promise<User> {
61
+ return dataOoc.correctorId && (await getRepository(User).findOneBy({ id: dataOoc.correctorId }))
62
+ }
63
+
64
+ @FieldResolver(type => User)
65
+ async reviewer(@Root() dataOoc: DataOoc): Promise<User> {
66
+ return dataOoc.reviewerId && (await getRepository(User).findOneBy({ id: dataOoc.reviewerId }))
67
+ }
68
+
59
69
  @FieldResolver(type => Domain)
60
70
  async domain(@Root() dataOoc: DataOoc): Promise<Domain> {
61
71
  return await getRepository(Domain).findOneBy({ id: dataOoc.domainId })
@@ -63,11 +73,11 @@ export class DataOocQuery {
63
73
 
64
74
  @FieldResolver(type => User)
65
75
  async updater(@Root() dataOoc: DataOoc): Promise<User> {
66
- return await getRepository(User).findOneBy({ id: dataOoc.updaterId })
76
+ return dataOoc.updaterId && (await getRepository(User).findOneBy({ id: dataOoc.updaterId }))
67
77
  }
68
78
 
69
79
  @FieldResolver(type => User)
70
80
  async creator(@Root() dataOoc: DataOoc): Promise<User> {
71
- return await getRepository(User).findOneBy({ id: dataOoc.creatorId })
81
+ return dataOoc.creatorId && (await getRepository(User).findOneBy({ id: dataOoc.creatorId }))
72
82
  }
73
83
  }