@things-factory/dataset 8.0.0-beta.9 → 8.0.0

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 (147) hide show
  1. package/client/activities/activity-data-collect-edit.ts +105 -0
  2. package/client/activities/activity-data-collect-view.ts +91 -0
  3. package/client/activities/activity-data-review-edit.ts +133 -0
  4. package/client/activities/activity-data-review-view.ts +145 -0
  5. package/client/activities/activity-ooc-resolve-edit.ts +195 -0
  6. package/client/activities/activity-ooc-resolve-view.ts +143 -0
  7. package/client/activities/activity-ooc-review-edit.ts +173 -0
  8. package/client/activities/activity-ooc-review-view.ts +129 -0
  9. package/client/bootstrap.ts +35 -0
  10. package/client/components/data-entry-form.ts +109 -0
  11. package/client/index.ts +1 -0
  12. package/client/pages/data-archive/data-archive-list-page.ts +277 -0
  13. package/client/pages/data-archive/data-archive-request-popup.ts +177 -0
  14. package/client/pages/data-entry/data-entry-list-page.ts +464 -0
  15. package/client/pages/data-key-set/data-key-item-list.ts +183 -0
  16. package/client/pages/data-key-set/data-key-set-importer.ts +89 -0
  17. package/client/pages/data-key-set/data-key-set-list-page.ts +413 -0
  18. package/client/pages/data-ooc/data-ooc-list-page.ts +549 -0
  19. package/client/pages/data-ooc/data-ooc-page.ts +164 -0
  20. package/client/pages/data-ooc/data-ooc-view.ts +236 -0
  21. package/client/pages/data-ooc/data-oocs-page.ts +200 -0
  22. package/client/pages/data-report/data-report-embed-page.ts +108 -0
  23. package/client/pages/data-report/data-report-list-page.ts +454 -0
  24. package/client/pages/data-report/data-report-samples-page.ts +174 -0
  25. package/client/pages/data-report/jasper-report-oocs-page.ts +110 -0
  26. package/client/pages/data-report/jasper-report-samples-crosstab-page.ts +110 -0
  27. package/client/pages/data-report/jasper-report-samples-page.ts +110 -0
  28. package/client/pages/data-sample/data-sample-list-page.ts +442 -0
  29. package/client/pages/data-sample/data-sample-page.ts +55 -0
  30. package/client/pages/data-sample/data-sample-search-page.ts +424 -0
  31. package/client/pages/data-sample/data-sample-view.ts +292 -0
  32. package/client/pages/data-sample/data-samples-page.ts +249 -0
  33. package/client/pages/data-sensor/data-sensor-list-page.ts +456 -0
  34. package/client/pages/data-set/data-item-list.ts +304 -0
  35. package/client/pages/data-set/data-set-importer.ts +89 -0
  36. package/client/pages/data-set/data-set-list-page.ts +1078 -0
  37. package/client/pages/data-summary/data-summary-list-page.ts +363 -0
  38. package/client/pages/data-summary/data-summary-period-page.ts +439 -0
  39. package/client/pages/data-summary/data-summary-search-page.ts +426 -0
  40. package/client/pages/data-summary/data-summary-view.ts +133 -0
  41. package/client/route.ts +91 -0
  42. package/client/tsconfig.json +13 -0
  43. package/dist-client/activities/activity-data-review-edit.d.ts +1 -5
  44. package/dist-client/activities/activity-data-review-edit.js +5 -143
  45. package/dist-client/activities/activity-data-review-edit.js.map +1 -1
  46. package/dist-client/pages/data-entry/data-entry-list-page.js +2 -2
  47. package/dist-client/pages/data-entry/data-entry-list-page.js.map +1 -1
  48. package/dist-client/tsconfig.tsbuildinfo +1 -1
  49. package/dist-server/activities/activity-data-review.js +5 -18
  50. package/dist-server/activities/activity-data-review.js.map +1 -1
  51. package/dist-server/activities/activity-ooc-review.js +52 -13
  52. package/dist-server/activities/activity-ooc-review.js.map +1 -1
  53. package/dist-server/controllers/create-data-sample.js +94 -4
  54. package/dist-server/controllers/create-data-sample.js.map +1 -1
  55. package/dist-server/controllers/index.d.ts +0 -3
  56. package/dist-server/controllers/index.js +0 -3
  57. package/dist-server/controllers/index.js.map +1 -1
  58. package/dist-server/service/data-sample/data-sample-query.d.ts +1 -1
  59. package/dist-server/service/data-sample/data-sample-query.js +3 -3
  60. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  61. package/dist-server/service/index.d.ts +1 -1
  62. package/dist-server/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +26 -26
  64. package/server/activities/activity-data-collect.ts +100 -0
  65. package/server/activities/activity-data-review.ts +82 -0
  66. package/server/activities/activity-ooc-resolve.ts +123 -0
  67. package/server/activities/activity-ooc-review.ts +144 -0
  68. package/server/activities/index.ts +11 -0
  69. package/server/controllers/create-data-sample.ts +426 -0
  70. package/server/controllers/data-use-case.ts +98 -0
  71. package/server/controllers/finalize-data-collection.ts +388 -0
  72. package/server/controllers/index.ts +3 -0
  73. package/server/controllers/issue-data-collection-task.ts +70 -0
  74. package/server/controllers/jasper-report.ts +186 -0
  75. package/server/controllers/query-data-summary-by-period.ts +178 -0
  76. package/server/controllers/shiny-report.ts +54 -0
  77. package/server/engine/index.ts +1 -0
  78. package/server/engine/task/create-data-sample.ts +100 -0
  79. package/server/engine/task/index.ts +2 -0
  80. package/server/engine/task/issue-collect-data.ts +45 -0
  81. package/server/index.ts +8 -0
  82. package/server/routes.ts +188 -0
  83. package/server/service/data-archive/data-archive-mutation.ts +273 -0
  84. package/server/service/data-archive/data-archive-query.ts +58 -0
  85. package/server/service/data-archive/data-archive-type.ts +48 -0
  86. package/server/service/data-archive/data-archive.ts +69 -0
  87. package/server/service/data-archive/index.ts +6 -0
  88. package/server/service/data-key-set/data-key-item-type.ts +31 -0
  89. package/server/service/data-key-set/data-key-set-mutation.ts +201 -0
  90. package/server/service/data-key-set/data-key-set-query.ts +68 -0
  91. package/server/service/data-key-set/data-key-set-type.ts +70 -0
  92. package/server/service/data-key-set/data-key-set.ts +86 -0
  93. package/server/service/data-key-set/index.ts +6 -0
  94. package/server/service/data-ooc/data-ooc-mutation.ts +154 -0
  95. package/server/service/data-ooc/data-ooc-query.ts +106 -0
  96. package/server/service/data-ooc/data-ooc-subscription.ts +48 -0
  97. package/server/service/data-ooc/data-ooc-type.ts +71 -0
  98. package/server/service/data-ooc/data-ooc.ts +259 -0
  99. package/server/service/data-ooc/index.ts +7 -0
  100. package/server/service/data-sample/data-sample-mutation.ts +18 -0
  101. package/server/service/data-sample/data-sample-query.ts +215 -0
  102. package/server/service/data-sample/data-sample-type.ts +47 -0
  103. package/server/service/data-sample/data-sample.ts +193 -0
  104. package/server/service/data-sample/index.ts +6 -0
  105. package/server/service/data-sensor/data-sensor-mutation.ts +116 -0
  106. package/server/service/data-sensor/data-sensor-query.ts +76 -0
  107. package/server/service/data-sensor/data-sensor-type.ts +104 -0
  108. package/server/service/data-sensor/data-sensor.ts +126 -0
  109. package/server/service/data-sensor/index.ts +6 -0
  110. package/server/service/data-set/data-item-type.ts +155 -0
  111. package/server/service/data-set/data-set-mutation.ts +552 -0
  112. package/server/service/data-set/data-set-query.ts +461 -0
  113. package/server/service/data-set/data-set-type.ts +204 -0
  114. package/server/service/data-set/data-set.ts +326 -0
  115. package/server/service/data-set/index.ts +6 -0
  116. package/server/service/data-set-history/data-set-history-query.ts +126 -0
  117. package/server/service/data-set-history/data-set-history-type.ts +12 -0
  118. package/server/service/data-set-history/data-set-history.ts +217 -0
  119. package/server/service/data-set-history/event-subscriber.ts +17 -0
  120. package/server/service/data-set-history/index.ts +7 -0
  121. package/server/service/data-spec/data-spec-manager.ts +21 -0
  122. package/server/service/data-spec/data-spec-query.ts +21 -0
  123. package/server/service/data-spec/data-spec.ts +45 -0
  124. package/server/service/data-spec/index.ts +5 -0
  125. package/server/service/data-summary/data-summary-mutation.ts +45 -0
  126. package/server/service/data-summary/data-summary-query.ts +179 -0
  127. package/server/service/data-summary/data-summary-type.ts +86 -0
  128. package/server/service/data-summary/data-summary.ts +170 -0
  129. package/server/service/data-summary/index.ts +7 -0
  130. package/server/service/index.ts +57 -0
  131. package/server/tsconfig.json +10 -0
  132. package/server/utils/config-resolver.ts +29 -0
  133. package/server/utils/index.ts +1 -0
  134. package/translations/en.json +0 -3
  135. package/translations/ja.json +0 -3
  136. package/translations/ko.json +0 -3
  137. package/translations/ms.json +0 -3
  138. package/translations/zh.json +0 -3
  139. package/dist-server/controllers/create-data-ooc.d.ts +0 -4
  140. package/dist-server/controllers/create-data-ooc.js +0 -65
  141. package/dist-server/controllers/create-data-ooc.js.map +0 -1
  142. package/dist-server/controllers/issue-ooc-resolve.d.ts +0 -3
  143. package/dist-server/controllers/issue-ooc-resolve.js +0 -49
  144. package/dist-server/controllers/issue-ooc-resolve.js.map +0 -1
  145. package/dist-server/controllers/issue-ooc-review.d.ts +0 -3
  146. package/dist-server/controllers/issue-ooc-review.js +0 -47
  147. package/dist-server/controllers/issue-ooc-review.js.map +0 -1
@@ -0,0 +1,178 @@
1
+ import { In } from 'typeorm'
2
+
3
+ import { getQueryBuilderFromListParams, getRepository, getTimesForPeriod, ListParam, Sorting } from '@things-factory/shell'
4
+
5
+ import { DataSet, DataSetSummaryPeriodType } from '../service/data-set/data-set'
6
+ import { DataSummary } from '../service/data-summary/data-summary'
7
+
8
+ const STAT_FUNCTION_MAP = {
9
+ sum: 'sum',
10
+ mean: 'mean',
11
+ stddev: 'standardDeviation',
12
+ variance: 'variance',
13
+ min: 'min',
14
+ max: 'max',
15
+ range: 'range',
16
+ median: 'median',
17
+ mode: 'mode'
18
+ }
19
+
20
+ export async function queryDataSummaryByPeriod(
21
+ period: 'today' | 'this month' | '30 days' | 'this year' | '12 months',
22
+ dataSetName: string,
23
+ dataKeys: string[] | null,
24
+ params: ListParam,
25
+ context: ResolverContext
26
+ ): Promise<DataSummary[]> {
27
+ const { domain, user, tx } = context.state
28
+ const { t } = context
29
+
30
+ const dataSet = await getRepository(DataSet).findOne({
31
+ where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, name: dataSetName },
32
+ relations: ['dataKeySet']
33
+ })
34
+
35
+ if (!dataSet) {
36
+ throw new Error(t('error.dataset not found', { dataSetName }))
37
+ }
38
+
39
+ // limitations
40
+ const summaryPeriodType = dataSet.summaryPeriod
41
+ if ((summaryPeriodType == DataSetSummaryPeriodType.Day || summaryPeriodType == DataSetSummaryPeriodType.WorkDate) && period == 'today') {
42
+ throw new Error(t('error.summary not supported', { dataSetName, period: t(`label.period-${period}`) }))
43
+ }
44
+
45
+ // dataKeys 가 설정되지 않았다면, dataSet의 dataKeySet을 그대로 적용한다는 의미임.
46
+ // dataKeys == [] 라면, dataKeySet을 적용하지 않는다는 의미임.
47
+ // dataKeys에는 dataKeySet의 dataKey 값을 따라야 한다.
48
+ const dataKeyItems = !dataSet.dataKeySet?.dataKeyItems
49
+ ? []
50
+ : dataKeys
51
+ ? dataKeys
52
+ .map(dataKey => {
53
+ return dataSet.dataKeySet.dataKeyItems.find(item => item.dataKey == dataKey)
54
+ })
55
+ .filter(Boolean)
56
+ : dataSet.dataKeySet.dataKeyItems
57
+
58
+ const searchables = dataKeyItems.map((item, index) => `key0${index + 1}`)
59
+ const dataItems = dataSet.dataItems.filter(item => item.agg)
60
+
61
+ const { from, to } = await getTimesForPeriod(period, context)
62
+
63
+ const selectPeriod = queryBuilder => {
64
+ if (period == 'today') {
65
+ queryBuilder.addSelect('summary.date', 'date').addSelect('summary.period', 'period')
66
+ } else if (['this year', '12 months'].includes(period)) {
67
+ queryBuilder.addSelect('SUBSTRING(summary.date, 1, 7) AS month')
68
+ } else {
69
+ queryBuilder.addSelect('summary.date', 'date')
70
+ }
71
+
72
+ return queryBuilder
73
+ }
74
+ const selectKeys = dataKeyItems.map((item, index) => {
75
+ const aliasName = `key0${index + 1}`
76
+ const columnName = `summary.${aliasName}`
77
+
78
+ return `${columnName} as ${aliasName}`
79
+ }) as string[]
80
+ const selectData = dataItems.map((item, index) => {
81
+ const aliasName = `data0${index + 1}`
82
+ const columnName = `summary.${aliasName}`
83
+
84
+ switch (item.agg) {
85
+ case STAT_FUNCTION_MAP.sum:
86
+ return `SUM(${columnName}) as ${aliasName}`
87
+ case STAT_FUNCTION_MAP.mean:
88
+ // 일차 mean 된 값들을 다시 계산하므로, 무의미할 수 있다.
89
+ return `AVG(${columnName}) as ${aliasName}`
90
+ case STAT_FUNCTION_MAP.stddev:
91
+ // 일차 stddev 된 값들을 다시 계산하므로, 무의미할 수 있다.
92
+ return `STDDEV(${columnName}) as ${aliasName}`
93
+ case STAT_FUNCTION_MAP.variance:
94
+ // 일차 variance 된 값들을 다시 계산하므로, 무의미할 수 있다.
95
+ return `VARIANCE(${columnName}) as ${aliasName}`
96
+ case STAT_FUNCTION_MAP.min:
97
+ return `MIN(${columnName}) as ${aliasName}`
98
+ case STAT_FUNCTION_MAP.max:
99
+ return `MAX(${columnName}) as ${aliasName}`
100
+ case STAT_FUNCTION_MAP.range:
101
+ // 일차 range 값들을 다시 계산하므로, 무의미할 수 있다.
102
+ return `MAX(${columnName}) - MIN(${columnName}) as ${aliasName}`
103
+ case STAT_FUNCTION_MAP.mode:
104
+ // not guaranteed
105
+ return `MODE() WITHIN GROUP (ORDER BY ${columnName})`
106
+ default:
107
+ return `AVG(${columnName}) as ${aliasName}`
108
+ }
109
+ })
110
+
111
+ const groupByPeriod =
112
+ period == 'today' ? 'summary.date, summary.period' : ['this year', '12 months'].includes(period) ? 'SUBSTRING(summary.date, 1, 7)' : 'summary.date'
113
+ const groupByKeys = dataKeyItems
114
+ .map((item, index) => {
115
+ return `summary.key0${index + 1}`
116
+ })
117
+ .join(', ')
118
+
119
+ const desc = false
120
+ var orderByPeriod: Sorting[] =
121
+ period == 'today'
122
+ ? [
123
+ {
124
+ name: 'date',
125
+ desc
126
+ },
127
+ {
128
+ name: 'period',
129
+ desc
130
+ }
131
+ ]
132
+ : ['this year', '12 months'].includes(period)
133
+ ? [{ name: 'month', desc }]
134
+ : [{ name: 'date', desc }]
135
+
136
+ if (params?.sortings && params.sortings.length > 0) {
137
+ orderByPeriod = params.sortings
138
+ }
139
+
140
+ var queryBuilder = getQueryBuilderFromListParams({
141
+ repository: getRepository(DataSummary),
142
+ params: {
143
+ ...params,
144
+ sortings: []
145
+ },
146
+ domain,
147
+ alias: 'summary',
148
+ searchables: searchables
149
+ })
150
+ .select('summary.dataSet')
151
+ .addSelect(selectData)
152
+ .addSelect('SUM(summary.count)', 'count')
153
+ .addSelect('SUM(summary.countOoc)', 'countOoc')
154
+ .addSelect('SUM(summary.countOos)', 'countOos')
155
+
156
+ queryBuilder = selectPeriod(queryBuilder)
157
+ .innerJoin('summary.dataSet', 'ds', 'ds.id = :dataSetName', {
158
+ dataSetName: dataSet.id
159
+ })
160
+ .andWhere('summary.domain = :domain', { domain: domain.id })
161
+ .andWhere('summary.date >= :from', { from })
162
+ .andWhere('summary.date < :to', { to })
163
+ .addGroupBy('summary.dataSet')
164
+ .addGroupBy(groupByPeriod)
165
+
166
+ if (dataKeyItems.length > 0) {
167
+ queryBuilder.addSelect(selectKeys).addGroupBy(groupByKeys)
168
+ }
169
+
170
+ orderByPeriod.map(orderBy => {
171
+ const { name: sort, desc } = orderBy
172
+ queryBuilder.addOrderBy(sort, desc ? 'DESC' : 'ASC')
173
+ })
174
+
175
+ const items = await queryBuilder.getRawMany()
176
+
177
+ return items
178
+ }
@@ -0,0 +1,54 @@
1
+ import { config } from '@things-factory/env'
2
+ import { getEndpointUrl } from '../utils/config-resolver'
3
+
4
+ import debugLib from 'debug'
5
+ const debug = debugLib('things-factory:dataset:shiny-report')
6
+ const dataReportConfig = config.get('dataReport')
7
+ const {
8
+ shiny: {
9
+ endpoint: ENDPOINT,
10
+ datasource: { database: DATABASE }
11
+ }
12
+ } = Object.assign(
13
+ {
14
+ shiny: {
15
+ endpoint: {},
16
+ datasource: {}
17
+ }
18
+ },
19
+ dataReportConfig
20
+ )
21
+
22
+ export async function renderShinyReport(context: any) {
23
+ debug('render:context', context)
24
+
25
+ try {
26
+ const cookies = new URLSearchParams(context.header.cookie.replace(/; /g, '&'))
27
+ const token = cookies.get('access_token')
28
+ const {
29
+ state: { domain },
30
+ query,
31
+ response
32
+ } = context
33
+
34
+ const urlParams = new URLSearchParams({
35
+ token,
36
+ ...query,
37
+ database: DATABASE,
38
+ domain: domain?.subdomain
39
+ }).toString()
40
+
41
+ const subpath = query?.reportView || ''
42
+
43
+ if (!subpath) {
44
+ return (context.body = '')
45
+ }
46
+
47
+ const url = getEndpointUrl(ENDPOINT, subpath)
48
+ const reportUrl = `${url}/?${urlParams}`
49
+
50
+ response.redirect(reportUrl)
51
+ } catch (ex) {
52
+ context.body = `Error: ${ex.message} || ''`
53
+ }
54
+ }
@@ -0,0 +1 @@
1
+ import './task'
@@ -0,0 +1,100 @@
1
+ import { TaskRegistry, InputStep, Context } from '@things-factory/integration-base'
2
+ import { getDataSource } from '@things-factory/shell'
3
+ import { access } from '@things-factory/utils'
4
+ import i18next from 'i18next'
5
+
6
+ import { DataSet } from '../../service/data-set/data-set'
7
+
8
+ import { createDataSample } from '../../controllers/create-data-sample'
9
+ import { TaskHandler } from '@things-factory/integration-base/dist-server/engine/types'
10
+
11
+ async function CreateDataSample(step: InputStep, context: Context) {
12
+ const { data, domain, user, lng } = context
13
+ var {
14
+ params: {
15
+ dataset: dataSetId,
16
+ source: sourceAccessor,
17
+ rawData: rawDataAccessor,
18
+ data: dataAccessor,
19
+ timestamp: timestampAccessor
20
+ }
21
+ } = step
22
+
23
+ if (!dataSetId) {
24
+ throw new Error(`no dataset found`)
25
+ }
26
+
27
+ // make new data-sample
28
+ return await getDataSource().transaction(async tx => {
29
+ const dataSet = await tx.getRepository(DataSet).findOne({
30
+ where: {
31
+ id: dataSetId
32
+ }
33
+ })
34
+
35
+ var source = await access(sourceAccessor, data)
36
+ var extractedData = await access(dataAccessor, data)
37
+ var extractedRawData = rawDataAccessor && (await access(rawDataAccessor, data))
38
+ var timestamp = timestampAccessor && (await access(timestampAccessor, data))
39
+
40
+ const dataSample = await createDataSample(
41
+ {
42
+ dataSet,
43
+ data: extractedData,
44
+ rawData: extractedRawData,
45
+ source,
46
+ collectedAt: new Date(timestamp || new Date())
47
+ },
48
+ {
49
+ t: i18next.t,
50
+ state: {
51
+ domain,
52
+ user,
53
+ lng,
54
+ tx
55
+ }
56
+ }
57
+ )
58
+
59
+ return {
60
+ data: dataSample
61
+ }
62
+ })
63
+ }
64
+
65
+ CreateDataSample.parameterSpec = [
66
+ {
67
+ type: 'entity-selector',
68
+ name: 'dataset',
69
+ label: 'data-set',
70
+ property: {
71
+ queryName: 'dataSets',
72
+ valueKey: 'id'
73
+ }
74
+ },
75
+ {
76
+ type: 'string',
77
+ name: 'source',
78
+ label: 'source'
79
+ },
80
+ {
81
+ type: 'string',
82
+ name: 'rawData',
83
+ label: 'raw-data'
84
+ },
85
+ {
86
+ type: 'string',
87
+ name: 'data',
88
+ label: 'data'
89
+ },
90
+ {
91
+ type: 'string',
92
+ name: 'timestamp',
93
+ label: 'timestamp'
94
+ }
95
+ ]
96
+
97
+ CreateDataSample.connectorFree = true
98
+ CreateDataSample.help = 'dataset/task/create-data-sample'
99
+
100
+ TaskRegistry.registerTaskHandler('create-data-sample', CreateDataSample as TaskHandler)
@@ -0,0 +1,2 @@
1
+ import './create-data-sample'
2
+ import './issue-collect-data'
@@ -0,0 +1,45 @@
1
+ import { TaskRegistry, InputStep, Context } from '@things-factory/integration-base'
2
+ import { issueDataCollectionTask } from '../../controllers/issue-data-collection-task'
3
+ import i18next from 'i18next'
4
+
5
+ async function IssueCollectData(step: InputStep, context: Context) {
6
+ const { logger, data, domain, user, lng } = context
7
+ var {
8
+ params: { dataset: dataSetId }
9
+ } = step
10
+
11
+ if (!dataSetId) {
12
+ throw new Error(`no dataset found`)
13
+ }
14
+
15
+ const activityInstance = await issueDataCollectionTask(domain.id, dataSetId, {
16
+ t: i18next.t,
17
+ state: {
18
+ domain,
19
+ user,
20
+ lng,
21
+ tx: undefined
22
+ }
23
+ })
24
+
25
+ return {
26
+ data: activityInstance
27
+ }
28
+ }
29
+
30
+ IssueCollectData.parameterSpec = [
31
+ {
32
+ type: 'entity-selector',
33
+ name: 'dataset',
34
+ label: 'data-set',
35
+ property: {
36
+ queryName: 'dataSets',
37
+ valueKey: 'id'
38
+ }
39
+ }
40
+ ]
41
+
42
+ IssueCollectData.connectorFree = true
43
+ IssueCollectData.help = 'dataset/task/issue-collect-data'
44
+
45
+ TaskRegistry.registerTaskHandler('issue-collect-data', IssueCollectData)
@@ -0,0 +1,8 @@
1
+ import './routes'
2
+ import './activities'
3
+ import './engine'
4
+
5
+ export * from './controllers'
6
+ export * from './service'
7
+
8
+ export * from './service/data-spec/data-spec-manager'
@@ -0,0 +1,188 @@
1
+ import { In } from 'typeorm'
2
+
3
+ import { Domain, getDataSource } from '@things-factory/shell'
4
+ import { User } from '@things-factory/auth-base'
5
+ import { runScenario } from '@things-factory/integration-base'
6
+ import { ScheduleRegisterRequest } from '@things-factory/scheduler-client'
7
+
8
+ import { createDataSample } from './controllers/create-data-sample'
9
+ import { renderJasperReport } from './controllers/jasper-report'
10
+ import { renderShinyReport } from './controllers/shiny-report'
11
+ import { issueDataCollectionTask } from './controllers/issue-data-collection-task'
12
+ import { DataSensor } from './service/data-sensor/data-sensor'
13
+ import { DataSet } from './service/data-set/data-set'
14
+
15
+ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
16
+ /*
17
+ * can add global public routes to application (auth not required, tenancy not required)
18
+ *
19
+ * ex) routes.get('/path', async(context, next) => {})
20
+ * ex) routes.post('/path', async(context, next) => {})
21
+ */
22
+
23
+ globalPublicRouter.post('/sensor-data', async (context, next) => {
24
+ // 데이타 검증
25
+ const { deviceId, data, rawData, timestamp = Date.now() } = context.request.body
26
+ if (!deviceId) {
27
+ throw new Error(`The deviceId(${deviceId}) property are mandatory`)
28
+ }
29
+
30
+ // make new data-sample
31
+ await getDataSource().transaction(async tx => {
32
+ // find sensor through deviceId
33
+ const sensor = await tx.getRepository(DataSensor).findOne({
34
+ where: { deviceId },
35
+ relations: ['domain', 'appliance', 'dataSet', 'decoder']
36
+ })
37
+
38
+ if (!sensor) {
39
+ throw new Error(`Sensor having given deviceId(${deviceId}) not found`)
40
+ }
41
+
42
+ if (!sensor.active) {
43
+ throw new Error(`State of the sensor given deviceId(${deviceId}) is not active`)
44
+ }
45
+
46
+ if (!sensor.appliance) {
47
+ throw new Error(`Appliance of the sensor given deviceId(${deviceId}) is not set up`)
48
+ }
49
+
50
+ const domain = sensor.domain
51
+ const dataSet = sensor.dataSet
52
+
53
+ /*
54
+ 원칙적으로 user 인증정보를 가지고 요청해야 하지만, 인증정보를 보낼 수 없는 상황이라고 전제하여 처리한다.
55
+ 앞으로, 보완되어야 한다.
56
+ */
57
+ const user: User =
58
+ context.user ||
59
+ (await tx.getRepository(User).findOne({
60
+ where: {
61
+ reference: sensor.appliance.id,
62
+ userType: 'appliance'
63
+ }
64
+ }))
65
+
66
+ context.state = {
67
+ user,
68
+ ...context.state,
69
+ domain,
70
+ tx
71
+ }
72
+
73
+ var decoded
74
+ if (sensor.decoder) {
75
+ const { name: scenarioName } = sensor.decoder
76
+ const variables = {
77
+ dataSensor: sensor,
78
+ source: deviceId,
79
+ data,
80
+ rawData,
81
+ timestamp
82
+ }
83
+
84
+ decoded = (await runScenario(null, scenarioName, variables, context))?.result
85
+ }
86
+
87
+ if (dataSet) {
88
+ await createDataSample(
89
+ {
90
+ dataSet,
91
+ data,
92
+ rawData,
93
+ source: deviceId,
94
+ collectedAt: new Date(timestamp),
95
+ ...decoded
96
+ },
97
+ context
98
+ )
99
+ }
100
+
101
+ context.status = 200
102
+ context.body = {
103
+ data,
104
+ ...decoded
105
+ }
106
+ })
107
+ })
108
+
109
+ /* When a callback occurs from the scheduler when a scheduled dataset is on schedule, data collection task for the dataset should be issued. */
110
+ globalPublicRouter.post('/callback-schedule-for-dataset', async (context, next) => {
111
+ const { client } = context.request.body as ScheduleRegisterRequest
112
+
113
+ if (!client || typeof client !== 'object') {
114
+ throw new Error('client property should be a part of callback body.')
115
+ }
116
+
117
+ const { group: domainId, key: dataSetId } = client
118
+
119
+ if (!domainId || !dataSetId) {
120
+ throw new Error(`group(${domainId}) and key(${dataSetId}) properties should not be empty`)
121
+ }
122
+
123
+ await issueDataCollectionTask(domainId, dataSetId, context)
124
+
125
+ context.status = 200
126
+ })
127
+
128
+ /* When a callback occurs from the scheduler when a scheduled summary is on schedule, data summary procedure for the dataset should be executed. */
129
+ globalPublicRouter.post('/callback-schedule-for-data-summary', async (context, next) => {
130
+ const { client } = context.request.body as ScheduleRegisterRequest
131
+
132
+ if (!client || typeof client !== 'object') {
133
+ throw new Error('client property should be a part of callback body.')
134
+ }
135
+
136
+ const { group: domainId, key: dataSetId } = client
137
+
138
+ if (!domainId || !dataSetId) {
139
+ throw new Error(`group(${domainId}) and key(${dataSetId}) properties should not be empty`)
140
+ }
141
+
142
+ await getDataSource().transaction(async tx => {
143
+ const domain = await tx.getRepository(Domain).findOneBy({ id: domainId })
144
+
145
+ if (!domain) {
146
+ throw new Error(`domain(${domainId}) not found`)
147
+ }
148
+
149
+ const dataSet = await tx
150
+ .getRepository(DataSet)
151
+ .findOne({ where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id: dataSetId } })
152
+
153
+ // do what you gotta do
154
+ })
155
+
156
+ context.status = 200
157
+ })
158
+ })
159
+
160
+ process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
161
+ /*
162
+ * can add global private routes to application (auth required, tenancy not required)
163
+ */
164
+ })
165
+
166
+ process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
167
+ /*
168
+ * can add domain public routes to application (auth not required, tenancy required)
169
+ */
170
+ })
171
+
172
+ process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
173
+ /*
174
+ * can add domain private routes to application (auth required, tenancy required)
175
+ */
176
+ domainPrivateRouter.get('/data-report/jasper', async (context, next) => {
177
+ context.body = await renderJasperReport(context)
178
+ })
179
+
180
+ domainPrivateRouter.get('/data-report/shiny', async (context, next) => {
181
+ await renderShinyReport(context)
182
+ })
183
+
184
+ domainPrivateRouter.get('/data-report/:reportType', async (context, next) => {
185
+ let { reportType } = context.params
186
+ context.body = `${reportType} Not Implemented.`
187
+ })
188
+ })