@things-factory/dataset 8.0.0-beta.1 → 8.0.0-beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. package/package.json +13 -13
  2. package/client/activities/activity-data-collect-edit.ts +0 -105
  3. package/client/activities/activity-data-collect-view.ts +0 -91
  4. package/client/activities/activity-data-review-edit.ts +0 -133
  5. package/client/activities/activity-data-review-view.ts +0 -145
  6. package/client/activities/activity-ooc-resolve-edit.ts +0 -195
  7. package/client/activities/activity-ooc-resolve-view.ts +0 -143
  8. package/client/activities/activity-ooc-review-edit.ts +0 -173
  9. package/client/activities/activity-ooc-review-view.ts +0 -129
  10. package/client/bootstrap.ts +0 -35
  11. package/client/components/data-entry-form.ts +0 -109
  12. package/client/index.ts +0 -1
  13. package/client/pages/data-archive/data-archive-list-page.ts +0 -277
  14. package/client/pages/data-archive/data-archive-request-popup.ts +0 -177
  15. package/client/pages/data-entry/data-entry-list-page.ts +0 -464
  16. package/client/pages/data-key-set/data-key-item-list.ts +0 -183
  17. package/client/pages/data-key-set/data-key-set-importer.ts +0 -89
  18. package/client/pages/data-key-set/data-key-set-list-page.ts +0 -413
  19. package/client/pages/data-ooc/data-ooc-list-page.ts +0 -549
  20. package/client/pages/data-ooc/data-ooc-page.ts +0 -164
  21. package/client/pages/data-ooc/data-ooc-view.ts +0 -236
  22. package/client/pages/data-ooc/data-oocs-page.ts +0 -200
  23. package/client/pages/data-report/data-report-embed-page.ts +0 -108
  24. package/client/pages/data-report/data-report-list-page.ts +0 -454
  25. package/client/pages/data-report/data-report-samples-page.ts +0 -174
  26. package/client/pages/data-report/jasper-report-oocs-page.ts +0 -110
  27. package/client/pages/data-report/jasper-report-samples-crosstab-page.ts +0 -110
  28. package/client/pages/data-report/jasper-report-samples-page.ts +0 -110
  29. package/client/pages/data-sample/data-sample-list-page.ts +0 -442
  30. package/client/pages/data-sample/data-sample-page.ts +0 -55
  31. package/client/pages/data-sample/data-sample-search-page.ts +0 -424
  32. package/client/pages/data-sample/data-sample-view.ts +0 -292
  33. package/client/pages/data-sample/data-samples-page.ts +0 -249
  34. package/client/pages/data-sensor/data-sensor-list-page.ts +0 -456
  35. package/client/pages/data-set/data-item-list.ts +0 -304
  36. package/client/pages/data-set/data-set-importer.ts +0 -89
  37. package/client/pages/data-set/data-set-list-page.ts +0 -1078
  38. package/client/pages/data-summary/data-summary-list-page.ts +0 -363
  39. package/client/pages/data-summary/data-summary-period-page.ts +0 -439
  40. package/client/pages/data-summary/data-summary-search-page.ts +0 -426
  41. package/client/pages/data-summary/data-summary-view.ts +0 -133
  42. package/client/route.ts +0 -91
  43. package/client/tsconfig.json +0 -13
  44. package/server/activities/activity-data-collect.ts +0 -100
  45. package/server/activities/activity-data-review.ts +0 -82
  46. package/server/activities/activity-ooc-resolve.ts +0 -123
  47. package/server/activities/activity-ooc-review.ts +0 -144
  48. package/server/activities/index.ts +0 -11
  49. package/server/controllers/create-data-sample.ts +0 -426
  50. package/server/controllers/data-use-case.ts +0 -98
  51. package/server/controllers/finalize-data-collection.ts +0 -388
  52. package/server/controllers/index.ts +0 -3
  53. package/server/controllers/issue-data-collection-task.ts +0 -70
  54. package/server/controllers/jasper-report.ts +0 -186
  55. package/server/controllers/query-data-summary-by-period.ts +0 -178
  56. package/server/controllers/shiny-report.ts +0 -54
  57. package/server/engine/index.ts +0 -1
  58. package/server/engine/task/create-data-sample.ts +0 -100
  59. package/server/engine/task/index.ts +0 -2
  60. package/server/engine/task/issue-collect-data.ts +0 -45
  61. package/server/index.ts +0 -8
  62. package/server/routes.ts +0 -188
  63. package/server/service/data-archive/data-archive-mutation.ts +0 -273
  64. package/server/service/data-archive/data-archive-query.ts +0 -58
  65. package/server/service/data-archive/data-archive-type.ts +0 -48
  66. package/server/service/data-archive/data-archive.ts +0 -69
  67. package/server/service/data-archive/index.ts +0 -6
  68. package/server/service/data-key-set/data-key-item-type.ts +0 -31
  69. package/server/service/data-key-set/data-key-set-mutation.ts +0 -201
  70. package/server/service/data-key-set/data-key-set-query.ts +0 -68
  71. package/server/service/data-key-set/data-key-set-type.ts +0 -70
  72. package/server/service/data-key-set/data-key-set.ts +0 -86
  73. package/server/service/data-key-set/index.ts +0 -6
  74. package/server/service/data-ooc/data-ooc-mutation.ts +0 -154
  75. package/server/service/data-ooc/data-ooc-query.ts +0 -106
  76. package/server/service/data-ooc/data-ooc-subscription.ts +0 -48
  77. package/server/service/data-ooc/data-ooc-type.ts +0 -71
  78. package/server/service/data-ooc/data-ooc.ts +0 -259
  79. package/server/service/data-ooc/index.ts +0 -7
  80. package/server/service/data-sample/data-sample-mutation.ts +0 -18
  81. package/server/service/data-sample/data-sample-query.ts +0 -215
  82. package/server/service/data-sample/data-sample-type.ts +0 -47
  83. package/server/service/data-sample/data-sample.ts +0 -193
  84. package/server/service/data-sample/index.ts +0 -6
  85. package/server/service/data-sensor/data-sensor-mutation.ts +0 -116
  86. package/server/service/data-sensor/data-sensor-query.ts +0 -76
  87. package/server/service/data-sensor/data-sensor-type.ts +0 -104
  88. package/server/service/data-sensor/data-sensor.ts +0 -126
  89. package/server/service/data-sensor/index.ts +0 -6
  90. package/server/service/data-set/data-item-type.ts +0 -155
  91. package/server/service/data-set/data-set-mutation.ts +0 -552
  92. package/server/service/data-set/data-set-query.ts +0 -461
  93. package/server/service/data-set/data-set-type.ts +0 -204
  94. package/server/service/data-set/data-set.ts +0 -326
  95. package/server/service/data-set/index.ts +0 -6
  96. package/server/service/data-set-history/data-set-history-query.ts +0 -126
  97. package/server/service/data-set-history/data-set-history-type.ts +0 -12
  98. package/server/service/data-set-history/data-set-history.ts +0 -217
  99. package/server/service/data-set-history/event-subscriber.ts +0 -17
  100. package/server/service/data-set-history/index.ts +0 -7
  101. package/server/service/data-spec/data-spec-manager.ts +0 -21
  102. package/server/service/data-spec/data-spec-query.ts +0 -21
  103. package/server/service/data-spec/data-spec.ts +0 -45
  104. package/server/service/data-spec/index.ts +0 -5
  105. package/server/service/data-summary/data-summary-mutation.ts +0 -45
  106. package/server/service/data-summary/data-summary-query.ts +0 -179
  107. package/server/service/data-summary/data-summary-type.ts +0 -86
  108. package/server/service/data-summary/data-summary.ts +0 -170
  109. package/server/service/data-summary/index.ts +0 -7
  110. package/server/service/index.ts +0 -57
  111. package/server/tsconfig.json +0 -10
  112. package/server/utils/config-resolver.ts +0 -29
  113. package/server/utils/index.ts +0 -1
@@ -1,388 +0,0 @@
1
- const statistics = require('simple-statistics')
2
- const deepClone = require('lodash/cloneDeep')
3
-
4
- import moment from 'moment-timezone'
5
- import { In } from 'typeorm'
6
-
7
- import { Sorting, getQueryBuilderFromListParams } from '@things-factory/shell'
8
- import { logger } from '@things-factory/env'
9
- import {
10
- getDateRangeForWorkDate,
11
- getDateRangeForWorkShift,
12
- getLatestWorkDateAndShift,
13
- getSummaryScheduleForWorkDate,
14
- getSummaryScheduleForWorkShift
15
- } from '@things-factory/work-shift'
16
-
17
- import { DataSample } from '../service/data-sample/data-sample'
18
- import { DataSet, DataSetSummaryPeriodType } from '../service/data-set/data-set'
19
-
20
- import { DataSummary } from '../service/data-summary/data-summary'
21
- import { DataKeyItem } from '../service/data-key-set/data-key-item-type'
22
-
23
- import { DataItem } from 'service'
24
-
25
- const STAT_FUNCTION_MAP = {
26
- sum: 'sum',
27
- mean: 'mean',
28
- stddev: 'standardDeviation',
29
- variance: 'variance',
30
- min: 'min',
31
- max: 'max',
32
- range: 'range',
33
- median: 'median',
34
- mode: 'mode'
35
- }
36
-
37
- const compareKeys = (dataKeyItems: DataKeyItem[], summary: Partial<DataSummary>, sample: DataSample): boolean => {
38
- return dataKeyItems.every((item, index) => {
39
- const prop = `key0${index + 1}`
40
- return sample[prop] === summary[prop]
41
- })
42
- }
43
-
44
- const buildKeysFromSample = (dataKeyItems: DataKeyItem[], sample: DataSample): Partial<DataSummary> => {
45
- return dataKeyItems.reduce((sum, item, index) => {
46
- const prop = `key0${index + 1}`
47
- sum[prop] = sample[prop]
48
-
49
- return sum
50
- }, {} as Partial<DataSummary>)
51
- }
52
-
53
- const buildKeySortingList = (dataKeyItems: DataKeyItem[]): Sorting[] => {
54
- return dataKeyItems.reduce((sum, item, index) => {
55
- const name = `key0${index + 1}`
56
- sum.push({ name, desc: true })
57
- return sum
58
- }, [])
59
- }
60
-
61
- const calculateSummary = (dataItems: DataItem[], base: { [tag: string]: any[] }) => {
62
- return dataItems.reduce((summary, item) => {
63
- const tag = item.tag
64
-
65
- const data = base[tag]
66
- .flat(Infinity)
67
- .map(Number)
68
- .filter(item => !isNaN(item))
69
-
70
- if (data.length > 0) {
71
- try {
72
- switch (item.stat) {
73
- case 'range':
74
- summary[tag] = statistics.max(data) - statistics.min(data)
75
- break
76
-
77
- default:
78
- const functionName = STAT_FUNCTION_MAP[item.stat]
79
- summary[tag] = (functionName && statistics[functionName](data)) || ''
80
- }
81
- } catch (err) {
82
- summary[tag] = null
83
- console.error(err)
84
- }
85
- } else {
86
- summary[tag] = null
87
- }
88
-
89
- return summary
90
- }, {})
91
- }
92
-
93
- const fillSummaryResult = (dataSummary: Partial<DataSummary>, dataItems: DataItem[], base: { [tag: string]: any[] }): void => {
94
- const summary = calculateSummary(dataItems, base)
95
-
96
- dataSummary.summary = summary
97
- dataItems.slice(0, 4).forEach((dataItem, idx) => {
98
- const value = Number(summary[dataItem.tag])
99
- dataSummary[`data0${idx + 1}`] = isNaN(value) ? null : value
100
- })
101
- }
102
-
103
- async function getLatestTimesForPeriod(periodType: DataSetSummaryPeriodType, context: ResolverContext): Promise<{ date?: string; period?: string; range: Date[] }> {
104
- const { domain } = context.state
105
- const now = moment()
106
-
107
- if (periodType == DataSetSummaryPeriodType.Hour) {
108
- const begin = now.clone().subtract(1, 'hour').startOf('hour')
109
- const end = now.clone().startOf('hour')
110
- const date = begin.clone().tz(domain.timezone)
111
-
112
- return {
113
- date: date.format('YYYY-MM-DD'),
114
- period: date.format('HH'),
115
- range: [begin.toDate(), end.toDate()]
116
- }
117
- } else if (periodType == DataSetSummaryPeriodType.WorkShift) {
118
- const { workDate, workShift, shiftRange } = await getLatestWorkDateAndShift(domain, new Date())
119
-
120
- return { date: workDate, period: workShift, range: shiftRange }
121
- } else if (periodType == DataSetSummaryPeriodType.WorkDate) {
122
- const { workDate, dateRange } = await getLatestWorkDateAndShift(domain, new Date())
123
-
124
- return { date: workDate, range: dateRange }
125
- } else if (periodType == DataSetSummaryPeriodType.Day) {
126
- const begin = now.clone().subtract(1, 'day').startOf('day')
127
- const end = now.clone().startOf('day')
128
- const date = begin.clone().tz(domain.timezone)
129
-
130
- return {
131
- date: date.format('YYYY-MM-DD'),
132
- range: [begin.toDate(), end.toDate()]
133
- }
134
- }
135
- }
136
-
137
- async function getTimesForPeriod(
138
- periodType: DataSetSummaryPeriodType,
139
- date: string,
140
- period: string,
141
- context: ResolverContext
142
- ): Promise<{ date?: string; period?: string; range: Date[] }> {
143
- const { domain } = context.state
144
-
145
- if (periodType == DataSetSummaryPeriodType.Hour) {
146
- const theDate = moment.tz(`${date} ${period}:00:00`, 'YYYY-MM-DD HH:mm:ss', domain.timezone)
147
-
148
- const begin = theDate.clone().startOf('hour').toDate()
149
- const end = theDate.clone().add(+1, 'hour').startOf('hour').toDate()
150
-
151
- return {
152
- date,
153
- period,
154
- range: [begin, end]
155
- }
156
- } else if (periodType == DataSetSummaryPeriodType.WorkShift) {
157
- const range = await getDateRangeForWorkShift(domain, date, period)
158
-
159
- return { date, period, range }
160
- } else if (periodType == DataSetSummaryPeriodType.WorkDate) {
161
- const range = await getDateRangeForWorkDate(domain, date)
162
-
163
- return { date, range }
164
- } else if (periodType == DataSetSummaryPeriodType.Day) {
165
- const theDate = moment.tz(`${date} 00:00:00`, 'YYYY-MM-DD HH:mm:ss', domain.timezone)
166
-
167
- const begin = theDate.clone().startOf('day').toDate()
168
- const end = theDate.clone().add(1, 'day').startOf('day').toDate()
169
-
170
- return {
171
- date: moment(begin).tz(domain.timezone).format('YYYY-MM-DD'),
172
- range: [begin, end]
173
- }
174
- }
175
- }
176
-
177
- export async function getDataFinalizeCrontabSchedule(dataSet: DataSet, context: ResolverContext): Promise<string> {
178
- const { domain, user, tx } = context.state
179
-
180
- try {
181
- const { summaryPeriod } = dataSet
182
-
183
- if (summaryPeriod == DataSetSummaryPeriodType.Hour) {
184
- return '0 5 * * * *'
185
- } else if (summaryPeriod == DataSetSummaryPeriodType.WorkShift) {
186
- return await getSummaryScheduleForWorkShift(domain)
187
- } else if (summaryPeriod == DataSetSummaryPeriodType.WorkDate) {
188
- return await getSummaryScheduleForWorkDate(domain)
189
- } else if (summaryPeriod == DataSetSummaryPeriodType.Day) {
190
- return '0 10 0 * * *'
191
- }
192
- } catch (err) {
193
- console.error(err)
194
- }
195
- }
196
-
197
- export async function finalizeLatestDataCollection(dataSetId: string, context: ResolverContext): Promise<boolean> {
198
- const { domain, user, tx } = context.state
199
-
200
- try {
201
- const dataSet = await tx.getRepository(DataSet).findOne({
202
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id: dataSetId },
203
- relations: ['dataKeySet']
204
- })
205
-
206
- const dataKeyItems = dataSet.dataKeySet?.dataKeyItems || []
207
- const dataItems = dataSet.dataItems.filter(item => item.stat)
208
- const initialSummary = dataItems.reduce((sum, item) => {
209
- sum[item.tag] = []
210
- return sum
211
- }, {})
212
-
213
- const { date, period, range } = await getLatestTimesForPeriod(dataSet.summaryPeriod, context)
214
- const limit = 100
215
- var page = 1
216
-
217
- var summaries: Partial<DataSummary>[] = []
218
- var summary: Partial<DataSummary>
219
-
220
- do {
221
- const samples = await getQueryBuilderFromListParams({
222
- repository: tx.getRepository(DataSample),
223
- domain,
224
- params: {
225
- filters: [{ name: 'dataSetId', operator: 'eq', value: dataSetId }],
226
- pagination: { page, limit },
227
- sortings: [...buildKeySortingList(dataKeyItems), { name: 'collectedAt', desc: true }]
228
- },
229
- alias: 'datasample'
230
- })
231
- // The 'Between' operator includes the 'to' time in the filtering, making it unsuitable for the desired use case.
232
- // .andWhere({ collectedAt: Between.apply(null, range) })
233
- .andWhere('datasample.collectedAt >= :from', { from: range[0] })
234
- .andWhere('datasample.collectedAt < :to', { to: range[1] })
235
- .getMany()
236
-
237
- for (const sample of samples) {
238
- if (!summary || !compareKeys(dataKeyItems, summary, sample)) {
239
- if (summary) {
240
- fillSummaryResult(summary, dataItems, summary.summary)
241
- summaries.push(summary)
242
- }
243
-
244
- summary = {
245
- domain,
246
- name: dataSet.name,
247
- description: dataSet.description,
248
- date,
249
- period,
250
- dataSet,
251
- ...buildKeysFromSample(dataKeyItems, sample),
252
- count: 0,
253
- countOoc: 0,
254
- countOos: 0,
255
- summary: deepClone(initialSummary),
256
- updater: user,
257
- creator: user
258
- }
259
- }
260
-
261
- summary.count++
262
- sample.ooc && summary.countOoc++
263
- sample.oos && summary.countOos++
264
-
265
- dataItems.forEach(item => {
266
- summary.summary[item.tag].push(sample.data[item.tag])
267
- })
268
- }
269
-
270
- if (samples.length < limit) {
271
- if (summary) {
272
- fillSummaryResult(summary, dataItems, summary.summary)
273
- summaries.push(summary)
274
- }
275
- break
276
- }
277
-
278
- page++
279
- } while (true)
280
-
281
- tx.getRepository(DataSummary).upsert(summaries, ['domain', 'dataSet', 'key01', 'key02', 'key03', 'key04', 'key05', 'date', 'period'])
282
-
283
- return true
284
- } catch (e) {
285
- logger.error(e)
286
- }
287
-
288
- return false
289
- }
290
-
291
- export async function finalizeDataCollection(dataSetId: string, date: string, period: string, context: ResolverContext): Promise<boolean> {
292
- const { domain, user, tx } = context.state
293
-
294
- try {
295
- const dataSet =
296
- dataSetId &&
297
- (await tx.getRepository(DataSet).findOne({
298
- where: { domain: In([domain.id, domain.parentId].filter(Boolean)), id: dataSetId },
299
- relations: ['dataKeySet']
300
- }))
301
-
302
- const dataKeyItems = dataSet.dataKeySet?.dataKeyItems || []
303
- const dataItems = dataSet.dataItems.filter(item => item.stat)
304
- const initialSummary = dataItems.reduce((sum, item) => {
305
- sum[item.tag] = []
306
- return sum
307
- }, {})
308
-
309
- const times = await getTimesForPeriod(dataSet.summaryPeriod, date, period, context)
310
- const range = times.range
311
- period = times.period
312
-
313
- const limit = 100
314
- var page = 1
315
-
316
- var summaries: Partial<DataSummary>[] = []
317
- var summary: Partial<DataSummary>
318
-
319
- do {
320
- const samples = await getQueryBuilderFromListParams({
321
- repository: tx.getRepository(DataSample),
322
- params: {
323
- filters: [{ name: 'dataSetId', operator: 'eq', value: dataSetId }],
324
- pagination: { page, limit },
325
- sortings: [...buildKeySortingList(dataKeyItems), { name: 'collectedAt', desc: true }]
326
- },
327
- domain,
328
- alias: 'datasample'
329
- })
330
- // The 'Between' operator includes the 'to' time in the filtering, making it unsuitable for the desired use case.
331
- // .andWhere({ collectedAt: Between.apply(null, range) })
332
- .andWhere('datasample.collectedAt >= :from', { from: range[0] })
333
- .andWhere('datasample.collectedAt < :to', { to: range[1] })
334
- .getMany()
335
-
336
- for (const sample of samples) {
337
- if (!summary || !compareKeys(dataKeyItems, summary, sample)) {
338
- if (summary) {
339
- fillSummaryResult(summary, dataItems, summary.summary)
340
- summaries.push(summary)
341
- }
342
-
343
- summary = {
344
- domain,
345
- name: dataSet.name,
346
- description: dataSet.description,
347
- date,
348
- period,
349
- dataSet,
350
- ...buildKeysFromSample(dataKeyItems, sample),
351
- count: 0,
352
- countOoc: 0,
353
- countOos: 0,
354
- summary: deepClone(initialSummary),
355
- updater: user,
356
- creator: user
357
- }
358
- }
359
-
360
- summary.count++
361
- sample.ooc && summary.countOoc++
362
- sample.oos && summary.countOos++
363
-
364
- dataItems.forEach(item => {
365
- summary.summary[item.tag].push(sample.data[item.tag])
366
- })
367
- }
368
-
369
- if (samples.length < limit) {
370
- if (summary) {
371
- fillSummaryResult(summary, dataItems, summary.summary)
372
- summaries.push(summary)
373
- }
374
- break
375
- }
376
-
377
- page++
378
- } while (true)
379
-
380
- tx.getRepository(DataSummary).upsert(summaries, ['domain', 'dataSet', 'key01', 'key02', 'key03', 'key04', 'key05', 'date', 'period'])
381
-
382
- return true
383
- } catch (e) {
384
- logger.error(e)
385
- }
386
-
387
- return false
388
- }
@@ -1,3 +0,0 @@
1
- export * from './create-data-sample'
2
- export * from './data-use-case'
3
- export * from './query-data-summary-by-period'
@@ -1,70 +0,0 @@
1
- import { In } from 'typeorm'
2
-
3
- import { Domain, getDataSource } from '@things-factory/shell'
4
- import { Activity, ActivityInstance, issue } from '@things-factory/worklist'
5
-
6
- import { DataSet } from '../service/data-set/data-set'
7
-
8
- export async function issueDataCollectionTask(
9
- domainId: string,
10
- dataSetId: string,
11
- context: ResolverContext
12
- ): Promise<ActivityInstance | void> {
13
- await getDataSource().transaction(async tx => {
14
- const domain = await tx.getRepository(Domain).findOneBy({ id: domainId })
15
-
16
- if (!domain) {
17
- throw new Error(`domain(${domainId}) not found`)
18
- }
19
-
20
- const dataSet = await tx.getRepository(DataSet).findOne({
21
- where: {
22
- domain: {
23
- id: In([domain.id, domain.parentId].filter(Boolean))
24
- },
25
- id: dataSetId
26
- },
27
- relations: ['entryRole']
28
- })
29
-
30
- const activity = (await tx.getRepository(Activity).findOneBy({
31
- domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },
32
- name: 'Collect Data'
33
- })) as Activity
34
-
35
- if (activity) {
36
- const { entryRole, timeLimit } = dataSet
37
-
38
- /* 해당 dataset에 대한 데이타 수집 태스크를 dataset entryRole에게 할당한다. */
39
- if (entryRole) {
40
- const activityInstance = {
41
- name: `[Data 수집] ${dataSet.name}`,
42
- description: dataSet.description,
43
- activityId: activity.id,
44
- dueAt: new Date(Date.now() + (timeLimit || activity.standardTime || 24 * 60 * 60) * 1000),
45
- input: {
46
- dataSetId: dataSet.id,
47
- dataSetName: dataSet.name
48
- },
49
- assigneeRole: entryRole,
50
- threadsMin: 1,
51
- threadsMax: 1
52
- }
53
-
54
- context.state = {
55
- ...context.state,
56
- domain,
57
- tx
58
- }
59
-
60
- return await issue(activityInstance, context)
61
- } else {
62
- throw new Error(
63
- `Data Entry Role not set. So Data Collect Activity for ${dataSet.name}($dataSet.id) could not be issued.`
64
- )
65
- }
66
- } else {
67
- throw new Error(`Data Collect Activity is not installed.`)
68
- }
69
- })
70
- }
@@ -1,186 +0,0 @@
1
- import FormData from 'form-data'
2
- import fetch from 'node-fetch'
3
-
4
- import { getEndpointUrl } from '../utils/config-resolver'
5
-
6
- import { STORAGE } from '@things-factory/attachment-base'
7
- import { AthenaController } from '@things-factory/aws-base'
8
- import { config } from '@things-factory/env'
9
-
10
- const dataReportConfig = config.get('dataReport')
11
- const {
12
- jasper: {
13
- endpoint: ENDPOINT,
14
- datasource: { database: DATABASE }
15
- }
16
- } = dataReportConfig || {
17
- jasper: {
18
- endpoint: {},
19
- datasource: {}
20
- }
21
- }
22
-
23
- function transformValuesToRows(queryResult) {
24
- var parseData = []
25
- let index = 1
26
- for (let i = 0; i < queryResult.Items.length; i++) {
27
- var j = 0
28
- const data = JSON.parse(queryResult.Items[i].data)
29
- const spec = JSON.parse(queryResult.Items[i].spec)
30
-
31
- for (let key in data) {
32
- if (Array.isArray(data[key])) {
33
- for (j = 0; j < data[key].length; j++) {
34
- for (let specKey in spec) {
35
- if (key === specKey) {
36
- parseData.push({
37
- item: spec[specKey].name,
38
- index: index + j,
39
- value: String(data[key][j])
40
- })
41
- }
42
- }
43
- }
44
- } else {
45
- parseData.push({
46
- item: key,
47
- index,
48
- value: String(data[key])
49
- })
50
- }
51
- }
52
- if (j !== 0) {
53
- index = index + j
54
- } else {
55
- index = index + 1
56
- }
57
- }
58
- }
59
-
60
- /** @todo considering trasformation in lambda, as massive dataset */
61
- function pivotData(rows) {
62
- let parsedData = []
63
- let index = 1
64
- for (let i = 0; i < rows.length; i++) {
65
- let j = 0
66
- const data = JSON.parse(rows[i].data)
67
- const spec = JSON.parse(rows[i].spec)
68
-
69
- for (let key in data) {
70
- /** @todo rule to display or not, about unspecified spec */
71
- const value = data[key]
72
- !spec[key]?.hidden &&
73
- parsedData.push({
74
- item: spec[key]?.name || key,
75
- index,
76
- value: Array.isArray(value) ? value.join(', ') : value
77
- })
78
- }
79
- if (j !== 0) {
80
- index = index + j
81
- } else {
82
- index = index + 1
83
- }
84
- }
85
-
86
- return parsedData
87
- }
88
-
89
- function parseJsonDataField(rows) {
90
- let parsedData = []
91
- for (let i = 0; i < rows.length; i++) {
92
- const row = rows[i]
93
- const data = JSON.parse(row.data)
94
- for (let key in data) {
95
- if (Array.isArray(data[key])) {
96
- data[key] = data[key].toString()
97
- }
98
- }
99
- delete row.data
100
- parsedData.push({ ...row, ...data })
101
- }
102
-
103
- return parsedData
104
- }
105
-
106
- const athenaClient = new AthenaController()
107
-
108
- async function queryAthena(params) {
109
- const { table, domain, datasetId, startDate, endDate, workShift, timezone } = params
110
- const queryData = {
111
- sql: `SELECT ds.name, ds.description, ds.data, dsh.data_items as spec, ds.workdate, ds.workshift,
112
- DATE_FORMAT(
113
- FROM_UNIXTIME(collected_at / 1000 / 1000) AT TIME ZONE '${timezone || 'UTC'}',
114
- '%Y-%m-%d %H:%i:%s'
115
- ) AS dscollected_at
116
- FROM ${table} ds
117
- JOIN data_set_histories dsh
118
- ON (ds.datasetid = dsh.original_id
119
- and ds.data_set_version = dsh.version)
120
- WHERE ds.domain='${domain}'
121
- AND ds.datasetid = '${datasetId}'
122
- AND ds.workdate >= '${startDate}'
123
- AND ds.workdate <= '${endDate}'
124
- ${workShift ? "AND ds.workshift = '" + workShift + "'" : ''}
125
- ORDER BY ds.collected_at`,
126
- db: DATABASE
127
- }
128
- // and json_extract_scalar(data, '$.dauid') = 'A8032AD81730'
129
-
130
- return await athenaClient.query(queryData)
131
- }
132
-
133
- export async function renderJasperReport(context: any) {
134
- const {
135
- state: { domain },
136
- query
137
- } = context
138
-
139
- const template = await STORAGE.readFile(query['reportTemplate'] || 'dynamic_header_sample.jrxml', 'utf-8')
140
- let templateType = query['templateType'] || 'crosstab'
141
- let parsedData = []
142
-
143
- // @todo: get dataset timezone
144
- /**
145
- * const variables = await gql(dataSet(id:${dataSetId}) {
146
- * name, description, partition_keys, timezone
147
- * })
148
- */
149
-
150
- query['domain'] = domain?.subdomain
151
- query['timezone'] = domain?.timezone
152
- const queryResult = await queryAthena(query)
153
- const rows = queryResult.Items
154
-
155
- if (!rows.length) {
156
- return '<h3>Not found result.</h3>'
157
- } else {
158
- const firstRow = rows[0]
159
- // uses the first row values as data-set has no history data.
160
- const parameters = {
161
- name: firstRow.name,
162
- description: firstRow.description,
163
- ...query
164
- }
165
-
166
- if (templateType === 'crosstab') {
167
- parsedData = pivotData(rows)
168
- } else {
169
- parsedData = parseJsonDataField(rows)
170
- }
171
-
172
- const formData = new FormData()
173
- formData.append('template', template)
174
- formData.append('jsonString', JSON.stringify(parsedData))
175
- formData.append('parameters', JSON.stringify(parameters))
176
-
177
- const { reportView } = query
178
- const url = getEndpointUrl(ENDPOINT, reportView)
179
- const response = await fetch(url, {
180
- method: 'POST',
181
- body: formData
182
- })
183
-
184
- return await response.text()
185
- }
186
- }