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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) 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 +278 -0
  4. package/client/activities/activity-data-review-view.ts +226 -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.js +19 -10
  44. package/dist-client/activities/activity-data-review-edit.js.map +1 -1
  45. package/dist-client/activities/activity-data-review-view.js +80 -0
  46. package/dist-client/activities/activity-data-review-view.js.map +1 -1
  47. package/dist-client/pages/data-entry/data-entry-list-page.js +2 -2
  48. package/dist-client/pages/data-entry/data-entry-list-page.js.map +1 -1
  49. package/dist-client/tsconfig.tsbuildinfo +1 -1
  50. package/dist-server/controllers/create-data-ooc.js +2 -0
  51. package/dist-server/controllers/create-data-ooc.js.map +1 -1
  52. package/dist-server/service/data-archive/index.d.ts +1 -1
  53. package/dist-server/service/data-ooc/index.d.ts +1 -1
  54. package/dist-server/service/data-sample/data-sample-query.d.ts +1 -1
  55. package/dist-server/service/data-sample/data-sample-query.js +3 -3
  56. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  57. package/dist-server/service/data-sample/index.d.ts +1 -1
  58. package/dist-server/service/data-set/index.d.ts +1 -1
  59. package/dist-server/service/index.d.ts +2 -2
  60. package/dist-server/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +26 -26
  62. package/server/activities/activity-data-collect.ts +100 -0
  63. package/server/activities/activity-data-review.ts +109 -0
  64. package/server/activities/activity-ooc-resolve.ts +123 -0
  65. package/server/activities/activity-ooc-review.ts +95 -0
  66. package/server/activities/index.ts +11 -0
  67. package/server/controllers/create-data-ooc.ts +80 -0
  68. package/server/controllers/create-data-sample.ts +323 -0
  69. package/server/controllers/data-use-case.ts +98 -0
  70. package/server/controllers/finalize-data-collection.ts +388 -0
  71. package/server/controllers/index.ts +6 -0
  72. package/server/controllers/issue-data-collection-task.ts +70 -0
  73. package/server/controllers/issue-ooc-resolve.ts +58 -0
  74. package/server/controllers/issue-ooc-review.ts +52 -0
  75. package/server/controllers/jasper-report.ts +186 -0
  76. package/server/controllers/query-data-summary-by-period.ts +178 -0
  77. package/server/controllers/shiny-report.ts +54 -0
  78. package/server/engine/index.ts +1 -0
  79. package/server/engine/task/create-data-sample.ts +100 -0
  80. package/server/engine/task/index.ts +2 -0
  81. package/server/engine/task/issue-collect-data.ts +45 -0
  82. package/server/index.ts +8 -0
  83. package/server/routes.ts +188 -0
  84. package/server/service/data-archive/data-archive-mutation.ts +273 -0
  85. package/server/service/data-archive/data-archive-query.ts +58 -0
  86. package/server/service/data-archive/data-archive-type.ts +48 -0
  87. package/server/service/data-archive/data-archive.ts +69 -0
  88. package/server/service/data-archive/index.ts +6 -0
  89. package/server/service/data-key-set/data-key-item-type.ts +31 -0
  90. package/server/service/data-key-set/data-key-set-mutation.ts +201 -0
  91. package/server/service/data-key-set/data-key-set-query.ts +68 -0
  92. package/server/service/data-key-set/data-key-set-type.ts +70 -0
  93. package/server/service/data-key-set/data-key-set.ts +86 -0
  94. package/server/service/data-key-set/index.ts +6 -0
  95. package/server/service/data-ooc/data-ooc-mutation.ts +154 -0
  96. package/server/service/data-ooc/data-ooc-query.ts +106 -0
  97. package/server/service/data-ooc/data-ooc-subscription.ts +48 -0
  98. package/server/service/data-ooc/data-ooc-type.ts +71 -0
  99. package/server/service/data-ooc/data-ooc.ts +259 -0
  100. package/server/service/data-ooc/index.ts +7 -0
  101. package/server/service/data-sample/data-sample-mutation.ts +18 -0
  102. package/server/service/data-sample/data-sample-query.ts +215 -0
  103. package/server/service/data-sample/data-sample-type.ts +47 -0
  104. package/server/service/data-sample/data-sample.ts +193 -0
  105. package/server/service/data-sample/index.ts +6 -0
  106. package/server/service/data-sensor/data-sensor-mutation.ts +116 -0
  107. package/server/service/data-sensor/data-sensor-query.ts +76 -0
  108. package/server/service/data-sensor/data-sensor-type.ts +104 -0
  109. package/server/service/data-sensor/data-sensor.ts +126 -0
  110. package/server/service/data-sensor/index.ts +6 -0
  111. package/server/service/data-set/data-item-type.ts +155 -0
  112. package/server/service/data-set/data-set-mutation.ts +552 -0
  113. package/server/service/data-set/data-set-query.ts +461 -0
  114. package/server/service/data-set/data-set-type.ts +204 -0
  115. package/server/service/data-set/data-set.ts +326 -0
  116. package/server/service/data-set/index.ts +6 -0
  117. package/server/service/data-set-history/data-set-history-query.ts +126 -0
  118. package/server/service/data-set-history/data-set-history-type.ts +12 -0
  119. package/server/service/data-set-history/data-set-history.ts +217 -0
  120. package/server/service/data-set-history/event-subscriber.ts +17 -0
  121. package/server/service/data-set-history/index.ts +7 -0
  122. package/server/service/data-spec/data-spec-manager.ts +21 -0
  123. package/server/service/data-spec/data-spec-query.ts +21 -0
  124. package/server/service/data-spec/data-spec.ts +45 -0
  125. package/server/service/data-spec/index.ts +5 -0
  126. package/server/service/data-summary/data-summary-mutation.ts +45 -0
  127. package/server/service/data-summary/data-summary-query.ts +179 -0
  128. package/server/service/data-summary/data-summary-type.ts +86 -0
  129. package/server/service/data-summary/data-summary.ts +170 -0
  130. package/server/service/data-summary/index.ts +7 -0
  131. package/server/service/index.ts +57 -0
  132. package/server/tsconfig.json +10 -0
  133. package/server/utils/config-resolver.ts +29 -0
  134. package/server/utils/index.ts +1 -0
@@ -0,0 +1,186 @@
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
+ }
@@ -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'