@operato/dataset 1.0.0-alpha.6 → 1.0.0-beta.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.
- package/CHANGELOG.md +428 -0
- package/demo/index.html +8 -90
- package/demo/ox-data-entry-form.html +118 -0
- package/demo/ox-data-item-spec.html +152 -0
- package/demo/ox-data-ooc-view.html +185 -0
- package/demo/ox-data-sample-view.html +150 -0
- package/demo/ox-grist-editor-data-item-spec.html +476 -0
- package/dist/src/grist-editor/index.d.ts +1 -0
- package/dist/src/grist-editor/index.js +7 -0
- package/dist/src/grist-editor/index.js.map +1 -0
- package/dist/src/grist-editor/ox-grist-editor-data-item-spec.d.ts +11 -0
- package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js +77 -0
- package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js.map +1 -0
- package/dist/src/grist-editor/ox-popup-data-item-spec.d.ts +13 -0
- package/dist/src/grist-editor/ox-popup-data-item-spec.js +90 -0
- package/dist/src/grist-editor/ox-popup-data-item-spec.js.map +1 -0
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/ox-data-entry-form.d.ts +1 -24
- package/dist/src/ox-data-entry-form.js +12 -16
- package/dist/src/ox-data-entry-form.js.map +1 -1
- package/dist/src/ox-data-item-spec.d.ts +18 -0
- package/dist/src/ox-data-item-spec.js +77 -0
- package/dist/src/ox-data-item-spec.js.map +1 -0
- package/dist/src/ox-data-ooc-view.d.ts +10 -0
- package/dist/src/ox-data-ooc-view.js +69 -0
- package/dist/src/ox-data-ooc-view.js.map +1 -0
- package/dist/src/ox-data-sample-view copy.d.ts +13 -0
- package/dist/src/ox-data-sample-view copy.js +214 -0
- package/dist/src/ox-data-sample-view copy.js.map +1 -0
- package/dist/src/ox-data-sample-view.d.ts +13 -0
- package/dist/src/ox-data-sample-view.js +168 -0
- package/dist/src/ox-data-sample-view.js.map +1 -0
- package/dist/src/ox-data-use-case.d.ts +16 -0
- package/dist/src/ox-data-use-case.js +111 -0
- package/dist/src/ox-data-use-case.js.map +1 -0
- package/dist/src/types.d.ts +78 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -12
- package/src/grist-editor/index.ts +10 -0
- package/src/grist-editor/ox-grist-editor-data-item-spec.ts +92 -0
- package/src/grist-editor/ox-popup-data-item-spec.ts +90 -0
- package/src/index.ts +6 -1
- package/src/ox-data-entry-form.ts +13 -37
- package/src/ox-data-item-spec.ts +74 -0
- package/src/ox-data-ooc-view.ts +67 -0
- package/src/ox-data-sample-view.ts +175 -0
- package/src/ox-data-use-case.ts +147 -0
- package/src/types.ts +72 -0
- package/themes/grist-theme.css +194 -0
- package/themes/oops-theme.css +26 -0
- package/themes/report-theme.css +47 -0
- package/translations/en.json +1 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
import '@operato/input/ox-input-file.js'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { css, html, LitElement } from 'lit'
|
|
4
4
|
import { customElement, property } from 'lit/decorators.js'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
type SelectOptions = SelectOption[]
|
|
8
|
-
type TypeOptions = {
|
|
9
|
-
options?: SelectOptions
|
|
10
|
-
[prop: string]: any
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type DataItem = {
|
|
14
|
-
name: string
|
|
15
|
-
description: string
|
|
16
|
-
sequence: number
|
|
17
|
-
tag: string
|
|
18
|
-
type: string
|
|
19
|
-
options: TypeOptions
|
|
20
|
-
quota: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type DataSet = {
|
|
24
|
-
name: string
|
|
25
|
-
description: string
|
|
26
|
-
dataItems: DataItem[]
|
|
27
|
-
}
|
|
6
|
+
import { DataSet } from './types.js'
|
|
28
7
|
|
|
29
8
|
@customElement('ox-data-entry-form')
|
|
30
9
|
export class OxDataEntryForm extends LitElement {
|
|
@@ -51,6 +30,7 @@ export class OxDataEntryForm extends LitElement {
|
|
|
51
30
|
background-color: var(--main-section-background-color);
|
|
52
31
|
padding: var(--padding-default) 0;
|
|
53
32
|
}
|
|
33
|
+
|
|
54
34
|
div[name] {
|
|
55
35
|
grid-column: span 2 / auto;
|
|
56
36
|
font: var(--label-font);
|
|
@@ -75,6 +55,7 @@ export class OxDataEntryForm extends LitElement {
|
|
|
75
55
|
padding: var(--input-field-padding);
|
|
76
56
|
font: var(--input-field-font);
|
|
77
57
|
}
|
|
58
|
+
|
|
78
59
|
@media only screen and (max-width: 460px) {
|
|
79
60
|
div[name] {
|
|
80
61
|
grid-column: span 3 / auto;
|
|
@@ -93,28 +74,28 @@ export class OxDataEntryForm extends LitElement {
|
|
|
93
74
|
}
|
|
94
75
|
|
|
95
76
|
private onChange(e: Event) {
|
|
96
|
-
|
|
77
|
+
this.value = this.buildValue()
|
|
97
78
|
|
|
98
79
|
this.dispatchEvent(
|
|
99
80
|
new CustomEvent('change', {
|
|
100
81
|
bubbles: true,
|
|
101
82
|
composed: true,
|
|
102
|
-
detail: value
|
|
83
|
+
detail: this.value
|
|
103
84
|
})
|
|
104
85
|
)
|
|
105
86
|
}
|
|
106
87
|
|
|
107
88
|
private buildInputs() {
|
|
108
|
-
const dataItems = this.dataSet!.dataItems
|
|
89
|
+
const dataItems = this.dataSet!.dataItems.filter(item => item.active)
|
|
109
90
|
|
|
110
91
|
return (dataItems || []).map(dataItem => {
|
|
111
|
-
const { name, description, tag, type, quota = 1, options = {} } = dataItem
|
|
92
|
+
const { name, description, tag, type, quota = 1, options = {}, unit } = dataItem
|
|
112
93
|
|
|
113
94
|
const samples = new Array(quota).fill(0)
|
|
114
|
-
const value = this.value
|
|
95
|
+
const value = this.value && this.value[tag]
|
|
115
96
|
|
|
116
97
|
const elements = samples.map((_, idx) => {
|
|
117
|
-
const v =
|
|
98
|
+
const v = value instanceof Array ? value[idx] : idx === 0 ? value : undefined
|
|
118
99
|
|
|
119
100
|
switch (type) {
|
|
120
101
|
case 'select':
|
|
@@ -150,7 +131,7 @@ export class OxDataEntryForm extends LitElement {
|
|
|
150
131
|
})
|
|
151
132
|
|
|
152
133
|
return html` <label .title=${description}>
|
|
153
|
-
<div name>${name}</div>
|
|
134
|
+
<div name>${name}${unit ? `(${unit})` : ''}</div>
|
|
154
135
|
<div elements>${elements}</div>
|
|
155
136
|
</label>`
|
|
156
137
|
})
|
|
@@ -160,19 +141,14 @@ export class OxDataEntryForm extends LitElement {
|
|
|
160
141
|
const dataItems = this.dataSet!.dataItems
|
|
161
142
|
|
|
162
143
|
return (dataItems || []).reduce((sum, dataItem) => {
|
|
163
|
-
const { tag,
|
|
144
|
+
const { tag, type } = dataItem
|
|
164
145
|
|
|
165
146
|
const editors = Array.prototype.slice.call(
|
|
166
147
|
this.renderRoot.querySelectorAll(`[name=${tag}]`) as NodeListOf<HTMLInputElement>
|
|
167
148
|
) as HTMLInputElement[]
|
|
168
149
|
|
|
169
150
|
if (editors.length > 0) {
|
|
170
|
-
sum[tag] =
|
|
171
|
-
editors.length > 1
|
|
172
|
-
? editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
|
|
173
|
-
: type === 'boolean'
|
|
174
|
-
? editors[0].checked
|
|
175
|
-
: editors[0].value
|
|
151
|
+
sum[tag] = editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
|
|
176
152
|
}
|
|
177
153
|
|
|
178
154
|
return sum
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import '@operato/property-editor/ox-properties-dynamic-view.js'
|
|
2
|
+
|
|
3
|
+
import { css, html, LitElement, PropertyValues } from 'lit'
|
|
4
|
+
import { customElement, property, queryAll, state } from 'lit/decorators.js'
|
|
5
|
+
|
|
6
|
+
import { OxDataUseCase } from './ox-data-use-case.js'
|
|
7
|
+
import { DataItem, DataItemSpecSet } from './types.js'
|
|
8
|
+
|
|
9
|
+
@customElement('ox-data-item-spec')
|
|
10
|
+
export class OxDataItemSpec extends LitElement {
|
|
11
|
+
static styles = css`
|
|
12
|
+
:host {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: row;
|
|
15
|
+
border-bottom: var(--border-dark-color);
|
|
16
|
+
padding: var(--margin-default) 0;
|
|
17
|
+
}
|
|
18
|
+
[specName] {
|
|
19
|
+
font: var(--legend-font);
|
|
20
|
+
color: var(--legend-text-color);
|
|
21
|
+
}
|
|
22
|
+
[description] {
|
|
23
|
+
font: var(--form-sublabel-font);
|
|
24
|
+
opacity: 0.8;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
form {
|
|
28
|
+
flex: 1;
|
|
29
|
+
}
|
|
30
|
+
`
|
|
31
|
+
|
|
32
|
+
@property({ type: Object }) value?: { [specSetName: string]: any }
|
|
33
|
+
@property({ type: Object }) dataItem?: DataItem
|
|
34
|
+
|
|
35
|
+
@state() dataItemSpecSets: DataItemSpecSet[] = []
|
|
36
|
+
|
|
37
|
+
@queryAll('ox-properties-dynamic-view') specSetViews!: NodeListOf<HTMLElement & { value: any }>
|
|
38
|
+
|
|
39
|
+
render() {
|
|
40
|
+
return html`<form @property-change=${(e: Event) => this.onChange(e)}>
|
|
41
|
+
${this.dataItemSpecSets.map(
|
|
42
|
+
({ name, description, specs }) => html` <div specName>${name}</div>
|
|
43
|
+
<div description>${description}</div>
|
|
44
|
+
<ox-properties-dynamic-view data-name=${name} .props=${specs} .value=${this.value?.[name]}>
|
|
45
|
+
</ox-properties-dynamic-view>`
|
|
46
|
+
)}
|
|
47
|
+
</form>`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
updated(changes: PropertyValues<this>) {
|
|
51
|
+
if (changes.has('dataItem')) {
|
|
52
|
+
this.dataItemSpecSets = OxDataUseCase.getUseCases().map(useCase => useCase.getSpecification(this.dataItem!))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private onChange(e: Event) {
|
|
57
|
+
this.value = this.buildValue()
|
|
58
|
+
|
|
59
|
+
this.dispatchEvent(
|
|
60
|
+
new CustomEvent('change', {
|
|
61
|
+
bubbles: true,
|
|
62
|
+
composed: true,
|
|
63
|
+
detail: { ...this.value }
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private buildValue() {
|
|
69
|
+
var value = {} as any
|
|
70
|
+
this.specSetViews.forEach(view => (value[view.getAttribute('data-name')!] = view.value))
|
|
71
|
+
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import '@operato/input/ox-input-file.js'
|
|
2
|
+
import './ox-data-sample-view'
|
|
3
|
+
|
|
4
|
+
import { DataOoc, DataSet } from './types.js'
|
|
5
|
+
import { LitElement, css, html } from 'lit'
|
|
6
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
7
|
+
|
|
8
|
+
@customElement('ox-data-ooc-view')
|
|
9
|
+
export class OxDataOocView extends LitElement {
|
|
10
|
+
static styles = css`
|
|
11
|
+
:host {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
background-color: var(--main-section-background-color);
|
|
15
|
+
padding: var(--padding-wide);
|
|
16
|
+
|
|
17
|
+
position: relative;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
h3 {
|
|
21
|
+
margin: var(--title-margin);
|
|
22
|
+
padding-top: 12px;
|
|
23
|
+
font: var(--title-font);
|
|
24
|
+
color: var(--title-text-color);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
h3[state] {
|
|
28
|
+
position: absolute;
|
|
29
|
+
|
|
30
|
+
right: 20px;
|
|
31
|
+
top: 25px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[page-description] {
|
|
35
|
+
margin: var(--page-description-margin);
|
|
36
|
+
font: var(--page-description-font);
|
|
37
|
+
color: var(--page-description-color);
|
|
38
|
+
}
|
|
39
|
+
`
|
|
40
|
+
|
|
41
|
+
@property({ type: Object }) dataSet?: DataSet
|
|
42
|
+
@property({ type: Object }) dataOoc?: DataOoc
|
|
43
|
+
|
|
44
|
+
render() {
|
|
45
|
+
const history = this.dataOoc?.history || []
|
|
46
|
+
const formatter = new Intl.DateTimeFormat(navigator.language, { dateStyle: 'full', timeStyle: 'short' })
|
|
47
|
+
|
|
48
|
+
return html`
|
|
49
|
+
<ox-data-sample-view .dataSample=${this.dataOoc} .dataSet=${this.dataSet}></ox-data-sample-view>
|
|
50
|
+
|
|
51
|
+
<h3 state>${this.dataOoc?.state || ''}</h3>
|
|
52
|
+
|
|
53
|
+
<h3>history</h3>
|
|
54
|
+
${history.map(
|
|
55
|
+
({ user, state, comment, timestamp }) => html`
|
|
56
|
+
<p page-description>
|
|
57
|
+
${formatter.format(new Date(timestamp))}
|
|
58
|
+
<br />
|
|
59
|
+
${state} by ${user.name}
|
|
60
|
+
<br />
|
|
61
|
+
${comment}
|
|
62
|
+
</p>
|
|
63
|
+
`
|
|
64
|
+
)}
|
|
65
|
+
`
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import '@operato/input/ox-input-file.js'
|
|
2
|
+
|
|
3
|
+
import { css, html, LitElement } from 'lit'
|
|
4
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
5
|
+
|
|
6
|
+
import { OxDataUseCase } from './ox-data-use-case.js'
|
|
7
|
+
import { DataItem, DataSample, DataSet } from './types.js'
|
|
8
|
+
|
|
9
|
+
@customElement('ox-data-sample-view')
|
|
10
|
+
export class OxDataSampleView extends LitElement {
|
|
11
|
+
static styles = css`
|
|
12
|
+
:host {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
background-color: var(--main-section-background-color);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
form {
|
|
19
|
+
flex: 1;
|
|
20
|
+
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
h2 {
|
|
26
|
+
margin: var(--title-margin);
|
|
27
|
+
padding-top: 25px;
|
|
28
|
+
font: var(--title-font);
|
|
29
|
+
color: var(--title-text-color);
|
|
30
|
+
}
|
|
31
|
+
[page-description] {
|
|
32
|
+
margin: var(--page-description-margin);
|
|
33
|
+
font: var(--page-description-font);
|
|
34
|
+
color: var(--page-description-color);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
table {
|
|
38
|
+
border-collapse: collapse;
|
|
39
|
+
margin-bottom: var(--margin-default);
|
|
40
|
+
}
|
|
41
|
+
th {
|
|
42
|
+
padding: var(--th-padding);
|
|
43
|
+
border-top: var(--th-border-top);
|
|
44
|
+
border-bottom: var(--td-border-bottom);
|
|
45
|
+
text-transform: var(--th-text-transform);
|
|
46
|
+
font: var(--th-font);
|
|
47
|
+
color: var(--th-color);
|
|
48
|
+
text-align: left;
|
|
49
|
+
}
|
|
50
|
+
th[item] {
|
|
51
|
+
min-width: 100px;
|
|
52
|
+
}
|
|
53
|
+
th[value] {
|
|
54
|
+
min-width: 100px;
|
|
55
|
+
}
|
|
56
|
+
tr {
|
|
57
|
+
background-color: var(--tr-background-color);
|
|
58
|
+
}
|
|
59
|
+
tr:nth-child(odd) {
|
|
60
|
+
background-color: var(--tr-background-odd-color);
|
|
61
|
+
}
|
|
62
|
+
tr:hover {
|
|
63
|
+
background-color: var(--tr-background-hover-color);
|
|
64
|
+
}
|
|
65
|
+
tr[ooc],
|
|
66
|
+
tr[oos] {
|
|
67
|
+
background-color: #fefbdf;
|
|
68
|
+
}
|
|
69
|
+
td {
|
|
70
|
+
border-bottom: var(--td-border-bottom);
|
|
71
|
+
padding: var(--td-padding);
|
|
72
|
+
font: var(--td-font);
|
|
73
|
+
color: var(--td-color);
|
|
74
|
+
}
|
|
75
|
+
td[name] {
|
|
76
|
+
font-weight: bold;
|
|
77
|
+
}
|
|
78
|
+
td mwc-icon {
|
|
79
|
+
color: var(--status-danger-color);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
pre {
|
|
83
|
+
tab-size: 2;
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
|
|
87
|
+
@property({ type: Object }) dataSet?: DataSet
|
|
88
|
+
@property({ type: Object }) dataSample?: DataSample
|
|
89
|
+
|
|
90
|
+
render() {
|
|
91
|
+
const dataItems = this.dataSet?.dataItems.filter(item => item.active) || []
|
|
92
|
+
const data = this.dataSample?.data || {}
|
|
93
|
+
const formatter = new Intl.DateTimeFormat(navigator.language, { dateStyle: 'full', timeStyle: 'short' })
|
|
94
|
+
|
|
95
|
+
const { name, description, collectedAt, workDate, workShift } = this.dataSample || {}
|
|
96
|
+
|
|
97
|
+
return html` <h2>${name}</h2>
|
|
98
|
+
<p page-description>
|
|
99
|
+
${description}<br /><br />
|
|
100
|
+
work date: ${workDate} ${workShift}<br />
|
|
101
|
+
collected at: ${formatter.format(new Date(collectedAt!))}
|
|
102
|
+
</p>
|
|
103
|
+
|
|
104
|
+
<form @change=${(e: Event) => this.onChange(e)}>
|
|
105
|
+
<table>
|
|
106
|
+
<tr>
|
|
107
|
+
<th item>item</th>
|
|
108
|
+
<th>description</th>
|
|
109
|
+
<th>unit</th>
|
|
110
|
+
<th value>value</th>
|
|
111
|
+
<th>specification</th>
|
|
112
|
+
<th>OOC</th>
|
|
113
|
+
<th>OOS</th>
|
|
114
|
+
</tr>
|
|
115
|
+
${dataItems.map(dataItem => {
|
|
116
|
+
const { ooc, oos } = this.evaluateOOC(dataItem, data?.[dataItem.tag]) || {}
|
|
117
|
+
const { name, description, unit } = dataItem
|
|
118
|
+
return html`
|
|
119
|
+
<tr ?ooc=${ooc} ?oos=${oos}>
|
|
120
|
+
<td name>${name}</td>
|
|
121
|
+
<td>${description}</td>
|
|
122
|
+
<td>${unit}</td>
|
|
123
|
+
<td>${this.buildValue(dataItem)}</td>
|
|
124
|
+
<td><pre>${this.buildSpec(dataItem)}</pre></td>
|
|
125
|
+
<td>${ooc ? html`<mwc-icon>done</mwc-icon>` : ''}</td>
|
|
126
|
+
<td>${oos ? html`<mwc-icon>done</mwc-icon>` : ''}</td>
|
|
127
|
+
</tr>
|
|
128
|
+
`
|
|
129
|
+
})}
|
|
130
|
+
</table>
|
|
131
|
+
</form>`
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private onChange(e: Event) {}
|
|
135
|
+
|
|
136
|
+
private buildSpec(dataItem: DataItem) {
|
|
137
|
+
return OxDataUseCase.elaborateDataItemSpec(this.dataSet!, dataItem)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private buildValue(dataItem: DataItem) {
|
|
141
|
+
const { tag, type } = dataItem
|
|
142
|
+
|
|
143
|
+
if (!this.dataSample) {
|
|
144
|
+
return ''
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const value = this.dataSample?.data[dataItem.tag]
|
|
148
|
+
if (value === undefined) {
|
|
149
|
+
return ''
|
|
150
|
+
}
|
|
151
|
+
const values = value instanceof Array ? value : [value]
|
|
152
|
+
|
|
153
|
+
const elements = values.map((_: any, idx) => {
|
|
154
|
+
const v = value[idx]
|
|
155
|
+
|
|
156
|
+
switch (type) {
|
|
157
|
+
case 'boolean':
|
|
158
|
+
return html` <input type="checkbox" name=${tag} .checked=${v} disabled />`
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
case 'select':
|
|
162
|
+
case 'number':
|
|
163
|
+
case 'string':
|
|
164
|
+
default:
|
|
165
|
+
return String(v === undefined ? '' : v)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
return type === 'boolean' ? elements : elements.join(', ')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private evaluateOOC(dataItem: DataItem, value: any) {
|
|
173
|
+
return OxDataUseCase.evaluateItem(this.dataSet!, dataItem, value)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { DataItem, DataItemSpecSet, DataSet, EvaluationResult } from './types'
|
|
2
|
+
|
|
3
|
+
export abstract class OxDataUseCase {
|
|
4
|
+
static registry: { [name: string]: OxDataUseCase } = {}
|
|
5
|
+
|
|
6
|
+
public static registerUseCase(name: string, useCase: OxDataUseCase) {
|
|
7
|
+
OxDataUseCase.registry[name] = useCase
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public static getUseCaseNames() {
|
|
11
|
+
return Object.keys(OxDataUseCase.registry)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public static getUseCases(): OxDataUseCase[] {
|
|
15
|
+
return Object.values(OxDataUseCase.registry)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public static getUseCase(name: string): OxDataUseCase | undefined {
|
|
19
|
+
return OxDataUseCase.registry[name]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public static elaborateDataItemSpec(dataSet: DataSet, dataItem: DataItem): string {
|
|
23
|
+
if (!dataSet.useCase) {
|
|
24
|
+
return ''
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const useCaseNames = dataSet.useCase.split(',').map(useCaseName => useCaseName.trim())
|
|
28
|
+
const useCases = useCaseNames.map(useCaseName => OxDataUseCase.getUseCase(useCaseName)).filter(useCase => !!useCase)
|
|
29
|
+
|
|
30
|
+
var texts = []
|
|
31
|
+
|
|
32
|
+
for (let j = 0; j < useCases.length; j++) {
|
|
33
|
+
const useCase = useCases[j]
|
|
34
|
+
if (!useCase) {
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const specs = dataItem.spec?.[dataSet.useCase]
|
|
39
|
+
if (!specs) {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = useCase.elaborateUseCaseSpec(dataItem.spec)
|
|
44
|
+
if (!result) {
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
texts.push(result)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return texts.join('\n')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public static evaluate(dataSet: DataSet, dataItems: DataItem[], data: any): EvaluationResult | undefined {
|
|
55
|
+
var ooc = false
|
|
56
|
+
var oos = false
|
|
57
|
+
|
|
58
|
+
if (!dataSet.useCase) {
|
|
59
|
+
return { ooc, oos }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const useCaseNames = dataSet.useCase.split(',').map(useCaseName => useCaseName.trim())
|
|
63
|
+
const useCases = useCaseNames.map(useCaseName => OxDataUseCase.getUseCase(useCaseName)).filter(useCase => !!useCase)
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < dataItems.length; i++) {
|
|
66
|
+
const dataItem = dataItems[i]
|
|
67
|
+
const { active, tag } = dataItem
|
|
68
|
+
if (!active || !tag) {
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let values: any | any[] = data[tag]
|
|
73
|
+
if (typeof values === 'undefined') {
|
|
74
|
+
continue // TODO what if in case no value ?
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!(values instanceof Array)) {
|
|
78
|
+
values = [values]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (let j = 0; j < useCases.length; j++) {
|
|
82
|
+
const useCase = useCases[j]
|
|
83
|
+
|
|
84
|
+
const specs = dataItem.spec?.[dataSet.useCase]
|
|
85
|
+
if (!specs) {
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = useCase?.evaluate(specs, values)
|
|
90
|
+
|
|
91
|
+
if (result) {
|
|
92
|
+
ooc ||= result.ooc
|
|
93
|
+
oos ||= result.oos
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { ooc, oos }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public static evaluateItem(dataSet: DataSet, dataItem: DataItem, values: any | any[]): EvaluationResult | undefined {
|
|
102
|
+
var ooc = false
|
|
103
|
+
var oos = false
|
|
104
|
+
|
|
105
|
+
if (!dataSet.useCase) {
|
|
106
|
+
return { ooc, oos }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const useCaseNames = dataSet.useCase.split(',').map(useCaseName => useCaseName.trim())
|
|
110
|
+
const useCases = useCaseNames.map(useCaseName => OxDataUseCase.getUseCase(useCaseName)).filter(useCase => !!useCase)
|
|
111
|
+
|
|
112
|
+
const { active, tag } = dataItem
|
|
113
|
+
if (!active || !tag) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof values === 'undefined') {
|
|
118
|
+
return // TODO what if in case no value ?
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!(values instanceof Array)) {
|
|
122
|
+
values = [values]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (let j = 0; j < useCases.length; j++) {
|
|
126
|
+
const useCase = useCases[j]
|
|
127
|
+
|
|
128
|
+
const specs = dataItem.spec?.[dataSet.useCase]
|
|
129
|
+
if (!specs) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = useCase?.evaluate(specs, values)
|
|
134
|
+
|
|
135
|
+
if (result) {
|
|
136
|
+
ooc ||= result.ooc
|
|
137
|
+
oos ||= result.oos
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { ooc, oos }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public abstract evaluate(specs: any, values: any[]): EvaluationResult | undefined
|
|
145
|
+
public abstract elaborateUseCaseSpec(spec: any): string | undefined
|
|
146
|
+
public abstract getSpecification(dataItem: DataItem): DataItemSpecSet
|
|
147
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type SelectOption = { text: string; value: string }
|
|
2
|
+
export type SelectOptions = SelectOption[]
|
|
3
|
+
export type TypeOptions = {
|
|
4
|
+
options?: SelectOptions
|
|
5
|
+
[prop: string]: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type DataItem = {
|
|
9
|
+
name: string
|
|
10
|
+
description: string
|
|
11
|
+
sequence: number
|
|
12
|
+
tag: string
|
|
13
|
+
type: string
|
|
14
|
+
active: boolean
|
|
15
|
+
options: TypeOptions
|
|
16
|
+
unit: string
|
|
17
|
+
quota: number
|
|
18
|
+
spec: { [useCase: string]: any }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type DataSet = {
|
|
22
|
+
name: string
|
|
23
|
+
description: string
|
|
24
|
+
type: 'manual' | 'automatic'
|
|
25
|
+
useCase: string
|
|
26
|
+
active: boolean
|
|
27
|
+
dataItems: DataItem[]
|
|
28
|
+
spec: { [dataItem: string]: { [useCase: string]: any } }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type DataItemSpec = {
|
|
32
|
+
type: string
|
|
33
|
+
label: string
|
|
34
|
+
name: string
|
|
35
|
+
property?: { [option: string]: any }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type DataItemSpecSet = {
|
|
39
|
+
name: string
|
|
40
|
+
description: string
|
|
41
|
+
help: string
|
|
42
|
+
specs: DataItemSpec[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type DataSample = {
|
|
46
|
+
name: string
|
|
47
|
+
description: string
|
|
48
|
+
data: any
|
|
49
|
+
spec: any
|
|
50
|
+
quota: number
|
|
51
|
+
workDate: string
|
|
52
|
+
workShift: string
|
|
53
|
+
collectedAt: Date
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type DataOocState = 'CREATED' | 'REVIEWED' | 'CORRECTED'
|
|
57
|
+
|
|
58
|
+
export type DataOoc = DataSample & {
|
|
59
|
+
state: DataOocState
|
|
60
|
+
correctiveAction: string
|
|
61
|
+
history: {
|
|
62
|
+
user: {
|
|
63
|
+
id: string
|
|
64
|
+
name: string
|
|
65
|
+
}
|
|
66
|
+
state: DataOocState
|
|
67
|
+
comment: string
|
|
68
|
+
timestamp: number
|
|
69
|
+
}[]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type EvaluationResult = { oos: boolean; ooc: boolean }
|