@things-factory/dataset 8.0.0-beta.9 → 8.0.2
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.
- package/client/activities/activity-data-collect-edit.ts +105 -0
- package/client/activities/activity-data-collect-view.ts +91 -0
- package/client/activities/activity-data-review-edit.ts +278 -0
- package/client/activities/activity-data-review-view.ts +226 -0
- package/client/activities/activity-ooc-resolve-edit.ts +195 -0
- package/client/activities/activity-ooc-resolve-view.ts +143 -0
- package/client/activities/activity-ooc-review-edit.ts +173 -0
- package/client/activities/activity-ooc-review-view.ts +129 -0
- package/client/bootstrap.ts +35 -0
- package/client/components/data-entry-form.ts +109 -0
- package/client/index.ts +1 -0
- package/client/pages/data-archive/data-archive-list-page.ts +277 -0
- package/client/pages/data-archive/data-archive-request-popup.ts +177 -0
- package/client/pages/data-entry/data-entry-list-page.ts +464 -0
- package/client/pages/data-key-set/data-key-item-list.ts +183 -0
- package/client/pages/data-key-set/data-key-set-importer.ts +89 -0
- package/client/pages/data-key-set/data-key-set-list-page.ts +413 -0
- package/client/pages/data-ooc/data-ooc-list-page.ts +549 -0
- package/client/pages/data-ooc/data-ooc-page.ts +164 -0
- package/client/pages/data-ooc/data-ooc-view.ts +236 -0
- package/client/pages/data-ooc/data-oocs-page.ts +200 -0
- package/client/pages/data-report/data-report-embed-page.ts +108 -0
- package/client/pages/data-report/data-report-list-page.ts +454 -0
- package/client/pages/data-report/data-report-samples-page.ts +174 -0
- package/client/pages/data-report/jasper-report-oocs-page.ts +110 -0
- package/client/pages/data-report/jasper-report-samples-crosstab-page.ts +110 -0
- package/client/pages/data-report/jasper-report-samples-page.ts +110 -0
- package/client/pages/data-sample/data-sample-list-page.ts +442 -0
- package/client/pages/data-sample/data-sample-page.ts +55 -0
- package/client/pages/data-sample/data-sample-search-page.ts +424 -0
- package/client/pages/data-sample/data-sample-view.ts +292 -0
- package/client/pages/data-sample/data-samples-page.ts +249 -0
- package/client/pages/data-sensor/data-sensor-list-page.ts +456 -0
- package/client/pages/data-set/data-item-list.ts +304 -0
- package/client/pages/data-set/data-set-importer.ts +89 -0
- package/client/pages/data-set/data-set-list-page.ts +1078 -0
- package/client/pages/data-summary/data-summary-list-page.ts +363 -0
- package/client/pages/data-summary/data-summary-period-page.ts +439 -0
- package/client/pages/data-summary/data-summary-search-page.ts +426 -0
- package/client/pages/data-summary/data-summary-view.ts +133 -0
- package/client/route.ts +91 -0
- package/client/tsconfig.json +13 -0
- package/dist-client/activities/activity-data-review-edit.js +19 -10
- package/dist-client/activities/activity-data-review-edit.js.map +1 -1
- package/dist-client/activities/activity-data-review-view.js +80 -0
- package/dist-client/activities/activity-data-review-view.js.map +1 -1
- package/dist-client/pages/data-entry/data-entry-list-page.js +2 -2
- package/dist-client/pages/data-entry/data-entry-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/create-data-ooc.js +2 -0
- package/dist-server/controllers/create-data-ooc.js.map +1 -1
- package/dist-server/service/data-archive/index.d.ts +1 -1
- package/dist-server/service/data-ooc/index.d.ts +1 -1
- package/dist-server/service/data-sample/data-sample-query.d.ts +1 -1
- package/dist-server/service/data-sample/data-sample-query.js +3 -3
- package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
- package/dist-server/service/data-sample/index.d.ts +1 -1
- package/dist-server/service/data-set/index.d.ts +1 -1
- package/dist-server/service/index.d.ts +2 -2
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +26 -26
- package/server/activities/activity-data-collect.ts +100 -0
- package/server/activities/activity-data-review.ts +109 -0
- package/server/activities/activity-ooc-resolve.ts +123 -0
- package/server/activities/activity-ooc-review.ts +95 -0
- package/server/activities/index.ts +11 -0
- package/server/controllers/create-data-ooc.ts +80 -0
- package/server/controllers/create-data-sample.ts +323 -0
- package/server/controllers/data-use-case.ts +98 -0
- package/server/controllers/finalize-data-collection.ts +388 -0
- package/server/controllers/index.ts +6 -0
- package/server/controllers/issue-data-collection-task.ts +70 -0
- package/server/controllers/issue-ooc-resolve.ts +58 -0
- package/server/controllers/issue-ooc-review.ts +52 -0
- package/server/controllers/jasper-report.ts +186 -0
- package/server/controllers/query-data-summary-by-period.ts +178 -0
- package/server/controllers/shiny-report.ts +54 -0
- package/server/engine/index.ts +1 -0
- package/server/engine/task/create-data-sample.ts +100 -0
- package/server/engine/task/index.ts +2 -0
- package/server/engine/task/issue-collect-data.ts +45 -0
- package/server/index.ts +8 -0
- package/server/routes.ts +188 -0
- package/server/service/data-archive/data-archive-mutation.ts +273 -0
- package/server/service/data-archive/data-archive-query.ts +58 -0
- package/server/service/data-archive/data-archive-type.ts +48 -0
- package/server/service/data-archive/data-archive.ts +69 -0
- package/server/service/data-archive/index.ts +6 -0
- package/server/service/data-key-set/data-key-item-type.ts +31 -0
- package/server/service/data-key-set/data-key-set-mutation.ts +201 -0
- package/server/service/data-key-set/data-key-set-query.ts +68 -0
- package/server/service/data-key-set/data-key-set-type.ts +70 -0
- package/server/service/data-key-set/data-key-set.ts +86 -0
- package/server/service/data-key-set/index.ts +6 -0
- package/server/service/data-ooc/data-ooc-mutation.ts +154 -0
- package/server/service/data-ooc/data-ooc-query.ts +106 -0
- package/server/service/data-ooc/data-ooc-subscription.ts +48 -0
- package/server/service/data-ooc/data-ooc-type.ts +71 -0
- package/server/service/data-ooc/data-ooc.ts +259 -0
- package/server/service/data-ooc/index.ts +7 -0
- package/server/service/data-sample/data-sample-mutation.ts +18 -0
- package/server/service/data-sample/data-sample-query.ts +215 -0
- package/server/service/data-sample/data-sample-type.ts +47 -0
- package/server/service/data-sample/data-sample.ts +193 -0
- package/server/service/data-sample/index.ts +6 -0
- package/server/service/data-sensor/data-sensor-mutation.ts +116 -0
- package/server/service/data-sensor/data-sensor-query.ts +76 -0
- package/server/service/data-sensor/data-sensor-type.ts +104 -0
- package/server/service/data-sensor/data-sensor.ts +126 -0
- package/server/service/data-sensor/index.ts +6 -0
- package/server/service/data-set/data-item-type.ts +155 -0
- package/server/service/data-set/data-set-mutation.ts +552 -0
- package/server/service/data-set/data-set-query.ts +461 -0
- package/server/service/data-set/data-set-type.ts +204 -0
- package/server/service/data-set/data-set.ts +326 -0
- package/server/service/data-set/index.ts +6 -0
- package/server/service/data-set-history/data-set-history-query.ts +126 -0
- package/server/service/data-set-history/data-set-history-type.ts +12 -0
- package/server/service/data-set-history/data-set-history.ts +217 -0
- package/server/service/data-set-history/event-subscriber.ts +17 -0
- package/server/service/data-set-history/index.ts +7 -0
- package/server/service/data-spec/data-spec-manager.ts +21 -0
- package/server/service/data-spec/data-spec-query.ts +21 -0
- package/server/service/data-spec/data-spec.ts +45 -0
- package/server/service/data-spec/index.ts +5 -0
- package/server/service/data-summary/data-summary-mutation.ts +45 -0
- package/server/service/data-summary/data-summary-query.ts +179 -0
- package/server/service/data-summary/data-summary-type.ts +86 -0
- package/server/service/data-summary/data-summary.ts +170 -0
- package/server/service/data-summary/index.ts +7 -0
- package/server/service/index.ts +57 -0
- package/server/tsconfig.json +10 -0
- package/server/utils/config-resolver.ts +29 -0
- package/server/utils/index.ts +1 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import moment from 'moment-timezone'
|
|
2
|
+
import { In } from 'typeorm'
|
|
3
|
+
|
|
4
|
+
import { Attachment, createAttachment } from '@things-factory/attachment-base'
|
|
5
|
+
import { Role } from '@things-factory/auth-base'
|
|
6
|
+
import { logger } from '@things-factory/env'
|
|
7
|
+
import { getRedirectSubdomainPath, pubsub } from '@things-factory/shell'
|
|
8
|
+
import { getWorkDateAndShift } from '@things-factory/work-shift'
|
|
9
|
+
import { Scenario, publishData } from '@things-factory/integration-base'
|
|
10
|
+
import { Activity } from '@things-factory/worklist'
|
|
11
|
+
import { issue } from '@things-factory/worklist/dist-server/controllers/activity-instance/issue'
|
|
12
|
+
|
|
13
|
+
import { DataSample } from '../service/data-sample/data-sample'
|
|
14
|
+
import { NewDataSample } from '../service/data-sample/data-sample-type'
|
|
15
|
+
import { DataSet } from '../service/data-set/data-set'
|
|
16
|
+
import { createDataOoc } from './create-data-ooc'
|
|
17
|
+
import { issueOocReview } from './issue-ooc-review'
|
|
18
|
+
import { DataUseCase } from './data-use-case'
|
|
19
|
+
|
|
20
|
+
// See README.md at ## Data Samples
|
|
21
|
+
process.env.TZ = 'UTC'
|
|
22
|
+
|
|
23
|
+
const fillDataKeys = (dataKeySet, data = {}) => {
|
|
24
|
+
const keys = dataKeySet?.dataKeyItems || []
|
|
25
|
+
return keys.reduce((sum, key, index) => {
|
|
26
|
+
const value = data[key.dataKey]
|
|
27
|
+
if (value != null) {
|
|
28
|
+
sum[`key0${index + 1}`] = value instanceof Array ? value[0] : value
|
|
29
|
+
}
|
|
30
|
+
return sum
|
|
31
|
+
}, {})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// parse variable javascript string pattern
|
|
35
|
+
const replaceVariables = (keys, dic) => {
|
|
36
|
+
for (const k in keys) {
|
|
37
|
+
const matches = keys[k].match(/\$\{\w*\}/g)
|
|
38
|
+
matches &&
|
|
39
|
+
matches.forEach(m => {
|
|
40
|
+
keys[k] = keys[k].replace(m, dic[m.slice(2, -1)])
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
return keys
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// It is required UTC date for Partitioning File System like AWS S3 from Athena.
|
|
47
|
+
// ex) %YYYY, %MM, %DD
|
|
48
|
+
const formatDate = (keys, _moment) => {
|
|
49
|
+
for (const k in keys) {
|
|
50
|
+
const matches = keys[k].match(/%\w*/g)
|
|
51
|
+
matches &&
|
|
52
|
+
matches.forEach(m => {
|
|
53
|
+
keys[k] = keys[k].replace(m, _moment.format(m.substr(1)))
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
return keys
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function createDataSample(newDataSample: NewDataSample, context: ResolverContext): Promise<DataSample> {
|
|
60
|
+
const { domain, user, tx } = context.state
|
|
61
|
+
|
|
62
|
+
const dataSet = await tx.getRepository(DataSet).findOne({
|
|
63
|
+
where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id: newDataSample.dataSet.id },
|
|
64
|
+
relations: ['dataKeySet']
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const { dataItems = [], tag: publishTag, normalScenarioId, outlierScenarioId } = dataSet
|
|
68
|
+
const collectedAt = newDataSample.collectedAt || new Date()
|
|
69
|
+
|
|
70
|
+
const timezone = dataSet.timezone || domain.timezone || 'UTC'
|
|
71
|
+
const format = 'YYYY-MM-DD'
|
|
72
|
+
|
|
73
|
+
// workDate ex) 2022-04-04
|
|
74
|
+
const { workDate, workShift } = await getWorkDateAndShift(domain, collectedAt, { timezone, format })
|
|
75
|
+
|
|
76
|
+
// local time dataSet timezone or domain timezone or default 'UTC'
|
|
77
|
+
|
|
78
|
+
const localDateTz = moment(collectedAt).tz(timezone)
|
|
79
|
+
const defaultPartitionKeys = {
|
|
80
|
+
domain: domain.subdomain,
|
|
81
|
+
datasetid: newDataSample.dataSet.id /* It should not be 'data_set_id' as column name duplicated for Glue */,
|
|
82
|
+
date: localDateTz.format(format) /* local time date */,
|
|
83
|
+
workdate: workDate /* working date */,
|
|
84
|
+
workshift: workShift
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var partitionKeys = {
|
|
88
|
+
...defaultPartitionKeys,
|
|
89
|
+
...dataSet.partitionKeys
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
partitionKeys = formatDate(partitionKeys, localDateTz)
|
|
93
|
+
partitionKeys = replaceVariables(partitionKeys, newDataSample.data)
|
|
94
|
+
|
|
95
|
+
const dataKeys = fillDataKeys(dataSet?.dataKeySet, newDataSample.data)
|
|
96
|
+
|
|
97
|
+
const { ooc, oos, judgment } = DataUseCase.evaluate(dataSet, dataItems, newDataSample.data) || {}
|
|
98
|
+
|
|
99
|
+
const old = await tx.getRepository(DataSample).findOne({
|
|
100
|
+
where: {
|
|
101
|
+
domain: { id: domain.id },
|
|
102
|
+
dataSet: { id: dataSet.id },
|
|
103
|
+
collectedAt,
|
|
104
|
+
...dataKeys
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
pre-processing for file attachment.
|
|
110
|
+
currently only support type of [FileUpload].
|
|
111
|
+
If [FileUpload[]] type needed, add 'files' type for dataset
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
const data = newDataSample.data
|
|
115
|
+
const attachments = []
|
|
116
|
+
|
|
117
|
+
for (let dataItem of dataItems) {
|
|
118
|
+
if (dataItem.type == 'file') {
|
|
119
|
+
const tag = dataItem.tag
|
|
120
|
+
const filesArray = data[tag]
|
|
121
|
+
|
|
122
|
+
if (tag && filesArray && filesArray.length > 0) {
|
|
123
|
+
let pathsArray = []
|
|
124
|
+
|
|
125
|
+
for (let files of filesArray) {
|
|
126
|
+
let paths = []
|
|
127
|
+
|
|
128
|
+
if (files instanceof Array) {
|
|
129
|
+
for (let file of files) {
|
|
130
|
+
if (file) {
|
|
131
|
+
const attachment = await createAttachment(
|
|
132
|
+
null,
|
|
133
|
+
{
|
|
134
|
+
attachment: {
|
|
135
|
+
file: file.file,
|
|
136
|
+
refType: DataSample.name
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
context
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const fetched = await tx.getRepository(Attachment).findOneBy({ id: attachment.id })
|
|
143
|
+
if (fetched) {
|
|
144
|
+
attachments.push(fetched)
|
|
145
|
+
paths.push({
|
|
146
|
+
id: fetched.id,
|
|
147
|
+
mimetype: fetched.mimetype,
|
|
148
|
+
name: fetched.name,
|
|
149
|
+
fullpath: fetched.fullpath
|
|
150
|
+
})
|
|
151
|
+
} else {
|
|
152
|
+
throw `Failed to save file(${attachment.name})`
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
paths.push(null)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pathsArray.push(paths)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
data[tag] = pathsArray
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const dataSample = await tx.getRepository(DataSample).save({
|
|
169
|
+
...old,
|
|
170
|
+
name: dataSet.name,
|
|
171
|
+
description: dataSet.description,
|
|
172
|
+
useCase: dataSet.useCase,
|
|
173
|
+
type: dataSet.type,
|
|
174
|
+
...newDataSample,
|
|
175
|
+
...dataKeys,
|
|
176
|
+
dataSetVersion: dataSet.version,
|
|
177
|
+
domain,
|
|
178
|
+
partitionKeys,
|
|
179
|
+
ooc,
|
|
180
|
+
oos,
|
|
181
|
+
judgment,
|
|
182
|
+
collectedAt,
|
|
183
|
+
workDate,
|
|
184
|
+
workShift,
|
|
185
|
+
creator: user,
|
|
186
|
+
updater: user
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
if (publishTag) {
|
|
190
|
+
publishData(publishTag, data, { domain, user })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* post-process for for file attachment. */
|
|
194
|
+
if (attachments.length > 0) {
|
|
195
|
+
attachments.forEach(attachment => (attachment.refId = dataSample.id))
|
|
196
|
+
tx.getRepository(Attachment).save(attachments)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (ooc || oos) {
|
|
200
|
+
const dataOoc = await createDataOoc(dataSample, dataSet, context)
|
|
201
|
+
await issueOocReview(dataOoc, dataSet, context)
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
pubsub.publish('notification', {
|
|
205
|
+
notification: {
|
|
206
|
+
domain,
|
|
207
|
+
type: 'error',
|
|
208
|
+
title: `[OOC] ${dataSet.name}`,
|
|
209
|
+
body: `Data OOC occurred on '${dataSet.name}'`,
|
|
210
|
+
url: getRedirectSubdomainPath(context, domain.subdomain, `/data-ooc/${dataOoc.id}`),
|
|
211
|
+
timestamp: collectedAt
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
} catch (err) {
|
|
215
|
+
logger.error('Notification', err)
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
if (normalScenarioId) {
|
|
219
|
+
const scenario = await tx.getRepository(Scenario).findOne({
|
|
220
|
+
where: {
|
|
221
|
+
domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },
|
|
222
|
+
id: normalScenarioId
|
|
223
|
+
},
|
|
224
|
+
relations: ['domain', 'steps', 'updater']
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
if (scenario) {
|
|
228
|
+
scenario.start({
|
|
229
|
+
instanceName: scenario.name + ':' + dataSet.name + ':' + dataSample.id,
|
|
230
|
+
domain,
|
|
231
|
+
user,
|
|
232
|
+
variables: {
|
|
233
|
+
dataSampleId: dataSample.id,
|
|
234
|
+
dataSet: dataSet.id,
|
|
235
|
+
data,
|
|
236
|
+
ooc,
|
|
237
|
+
oos,
|
|
238
|
+
judgment,
|
|
239
|
+
collectedAt,
|
|
240
|
+
workDate,
|
|
241
|
+
workShift,
|
|
242
|
+
domain: {
|
|
243
|
+
id: domain.id,
|
|
244
|
+
subdomain: domain.subdomain,
|
|
245
|
+
name: domain.name
|
|
246
|
+
},
|
|
247
|
+
updator: {
|
|
248
|
+
id: user.id,
|
|
249
|
+
email: user.email,
|
|
250
|
+
name: user.name
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
} else {
|
|
255
|
+
console.error(`Cannot find the set normal-scenario for the dataset(${dataSet.name}).`)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (dataSet.requiresReview) {
|
|
260
|
+
const activity = (await tx.getRepository(Activity).findOneBy({
|
|
261
|
+
domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },
|
|
262
|
+
name: 'Data Review'
|
|
263
|
+
})) as Activity
|
|
264
|
+
|
|
265
|
+
if (activity) {
|
|
266
|
+
const assigneeRole =
|
|
267
|
+
dataSet.supervisoryRoleId &&
|
|
268
|
+
(await tx.getRepository(Role).findOneBy({
|
|
269
|
+
domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },
|
|
270
|
+
id: dataSet.supervisoryRoleId
|
|
271
|
+
}))
|
|
272
|
+
|
|
273
|
+
// const assignees = dataSet.supervisoryRoleId
|
|
274
|
+
// ? [{ type: 'Role', value: dataSet.supervisoryRoleId, assigneeRole }]
|
|
275
|
+
// : []
|
|
276
|
+
|
|
277
|
+
/* 해당 dataset의 supervisor로 하여금, data를 리뷰하고 instruction을 작성해서, approvalLine을 이용해서 승인을 한다. */
|
|
278
|
+
if (assigneeRole) {
|
|
279
|
+
dataSample.dataItems = dataItems
|
|
280
|
+
const activityInstance = {
|
|
281
|
+
name: `[Data 검토] ${dataSet.name}`,
|
|
282
|
+
description: dataSet.description,
|
|
283
|
+
activityId: activity.id,
|
|
284
|
+
dueAt: new Date(collectedAt.getTime() + (activity.standardTime || 24 * 60 * 60) * 1000),
|
|
285
|
+
input: {
|
|
286
|
+
dataSampleId: dataSample.id
|
|
287
|
+
},
|
|
288
|
+
assigneeRole,
|
|
289
|
+
threadsMin: 1,
|
|
290
|
+
threadsMax: 1,
|
|
291
|
+
approvalLine: dataSet.reviewApprovalLine
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
dataSample.reviewActivityInstance = await issue(activityInstance as any, context)
|
|
295
|
+
await tx.getRepository(DataSample).save(dataSample)
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
pubsub.publish('notification', {
|
|
299
|
+
notification: {
|
|
300
|
+
domain,
|
|
301
|
+
type: 'info',
|
|
302
|
+
title: `[Data Review] ${dataSet.name}`,
|
|
303
|
+
body: `Data Review occurred on '${dataSet.name}'`,
|
|
304
|
+
url: getRedirectSubdomainPath(context, domain.subdomain, `/data-sample/${dataSample.id}`),
|
|
305
|
+
timestamp: collectedAt
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
} catch (err) {
|
|
309
|
+
logger.error('Notification', err)
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
console.error(
|
|
313
|
+
`Assignees are not set. So Data Review task for ${dataSet.name}(${dataSet.id}) could not be issued.`
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
console.error('Data Review Activity not installed.')
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return dataSample
|
|
323
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { DataItem } from '../service/data-set/data-item-type'
|
|
2
|
+
import { DataSet } from '../service/data-set/data-set'
|
|
3
|
+
|
|
4
|
+
export type DataItemSpec = {
|
|
5
|
+
type: string
|
|
6
|
+
label: string
|
|
7
|
+
name: string
|
|
8
|
+
property?: { [option: string]: any }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type DataItemSpecSet = {
|
|
12
|
+
name: string
|
|
13
|
+
description: string
|
|
14
|
+
help?: string
|
|
15
|
+
specs: DataItemSpec[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type EvaluationResult = {
|
|
19
|
+
oos: boolean
|
|
20
|
+
ooc: boolean
|
|
21
|
+
judgment?: { [tag: string]: { ooc: boolean; oos: boolean } }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export abstract class DataUseCase {
|
|
25
|
+
static registry: { [name: string]: DataUseCase } = {}
|
|
26
|
+
|
|
27
|
+
public static registerUseCase(name: string, provider: DataUseCase) {
|
|
28
|
+
DataUseCase.registry[name] = provider
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static getUseCaseNames() {
|
|
32
|
+
return Object.keys(DataUseCase.registry)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static getUseCase(name: string): DataUseCase | undefined {
|
|
36
|
+
return DataUseCase.registry[name]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public static evaluate(dataSet: DataSet, dataItems: DataItem[], data: any): EvaluationResult {
|
|
40
|
+
var ooc = false
|
|
41
|
+
var oos = false
|
|
42
|
+
var judgment: { [tag: string]: { ooc: boolean; oos: boolean } } = {}
|
|
43
|
+
|
|
44
|
+
if (!dataSet.useCase) {
|
|
45
|
+
return { ooc, oos }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const useCaseNames = dataSet.useCase.split(',').map(useCaseName => useCaseName.trim())
|
|
49
|
+
const useCases = useCaseNames.map(useCaseName => DataUseCase.getUseCase(useCaseName)).filter(useCase => !!useCase)
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < dataItems.length; i++) {
|
|
52
|
+
const dataItem = dataItems[i]
|
|
53
|
+
const { active, tag } = dataItem
|
|
54
|
+
if (!active || !tag) {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let values: any | any[] = data && data[tag]
|
|
59
|
+
if (values == null) {
|
|
60
|
+
continue // TODO what if in case no value ?
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!(values instanceof Array)) {
|
|
64
|
+
values = [values]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let oocForTag = false
|
|
68
|
+
let oosForTag = false
|
|
69
|
+
|
|
70
|
+
for (let j = 0; j < useCases.length; j++) {
|
|
71
|
+
const useCase = useCases[j]
|
|
72
|
+
|
|
73
|
+
const specs = dataItem.spec?.[dataSet.useCase]
|
|
74
|
+
if (!specs) {
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const result = useCase.evaluate(specs, values)
|
|
79
|
+
|
|
80
|
+
if (result) {
|
|
81
|
+
oocForTag ||= result.ooc
|
|
82
|
+
oosForTag ||= result.oos
|
|
83
|
+
ooc ||= result.ooc
|
|
84
|
+
oos ||= result.oos
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
judgment[tag] = {
|
|
89
|
+
ooc: oocForTag,
|
|
90
|
+
oos: oosForTag
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { ooc, oos, judgment }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public abstract evaluate(specs: any, values: any[]): EvaluationResult
|
|
98
|
+
}
|