@operato/dataset 1.0.0-alpha.4 → 1.0.0-alpha.40
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 +330 -0
- package/demo/index.html +11 -95
- 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 +473 -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 +58 -17
- 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 +165 -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 +76 -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 +19 -11
- 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 +59 -38
- 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 +171 -0
- package/src/ox-data-use-case.ts +147 -0
- package/src/types.ts +70 -0
- package/themes/app-theme.css +142 -0
- package/themes/form-theme.css +75 -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 {
|
|
@@ -35,9 +14,56 @@ export class OxDataEntryForm extends LitElement {
|
|
|
35
14
|
}
|
|
36
15
|
|
|
37
16
|
form {
|
|
17
|
+
flex: 1;
|
|
18
|
+
|
|
38
19
|
display: flex;
|
|
39
20
|
flex-direction: column;
|
|
40
21
|
}
|
|
22
|
+
label {
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: repeat(12, 1fr);
|
|
25
|
+
gap: 9px;
|
|
26
|
+
align-items: center;
|
|
27
|
+
margin-bottom: var(--margin-default);
|
|
28
|
+
}
|
|
29
|
+
label:nth-child(even) {
|
|
30
|
+
background-color: var(--main-section-background-color);
|
|
31
|
+
padding: var(--padding-default) 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
div[name] {
|
|
35
|
+
grid-column: span 2 / auto;
|
|
36
|
+
font: var(--label-font);
|
|
37
|
+
color: var(--label-color);
|
|
38
|
+
text-align: right;
|
|
39
|
+
}
|
|
40
|
+
div[elements] {
|
|
41
|
+
grid-column: span 10 / auto;
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: row;
|
|
44
|
+
flex-wrap: wrap;
|
|
45
|
+
gap: 10px;
|
|
46
|
+
padding-right: var(--padding-default);
|
|
47
|
+
}
|
|
48
|
+
div[elements] * {
|
|
49
|
+
flex: 1;
|
|
50
|
+
}
|
|
51
|
+
div[elements] input,
|
|
52
|
+
div[elements] select {
|
|
53
|
+
border: var(--input-field-border);
|
|
54
|
+
border-radius: var(--input-field-border-radius);
|
|
55
|
+
padding: var(--input-field-padding);
|
|
56
|
+
font: var(--input-field-font);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media only screen and (max-width: 460px) {
|
|
60
|
+
div[name] {
|
|
61
|
+
grid-column: span 3 / auto;
|
|
62
|
+
}
|
|
63
|
+
div[elements] {
|
|
64
|
+
grid-column: span 9 / auto;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
41
67
|
`
|
|
42
68
|
|
|
43
69
|
@property({ type: Object }) dataSet?: DataSet
|
|
@@ -48,28 +74,28 @@ export class OxDataEntryForm extends LitElement {
|
|
|
48
74
|
}
|
|
49
75
|
|
|
50
76
|
private onChange(e: Event) {
|
|
51
|
-
|
|
77
|
+
this.value = this.buildValue()
|
|
52
78
|
|
|
53
79
|
this.dispatchEvent(
|
|
54
80
|
new CustomEvent('change', {
|
|
55
81
|
bubbles: true,
|
|
56
82
|
composed: true,
|
|
57
|
-
detail: value
|
|
83
|
+
detail: this.value
|
|
58
84
|
})
|
|
59
85
|
)
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
private buildInputs() {
|
|
63
|
-
const dataItems = this.dataSet!.dataItems
|
|
89
|
+
const dataItems = this.dataSet!.dataItems.filter(item => item.active)
|
|
64
90
|
|
|
65
91
|
return (dataItems || []).map(dataItem => {
|
|
66
|
-
const { name, description, tag, type, quota = 1, options = {} } = dataItem
|
|
92
|
+
const { name, description, tag, type, quota = 1, options = {}, unit } = dataItem
|
|
67
93
|
|
|
68
94
|
const samples = new Array(quota).fill(0)
|
|
69
|
-
const value = this.value
|
|
95
|
+
const value = this.value && this.value[tag]
|
|
70
96
|
|
|
71
97
|
const elements = samples.map((_, idx) => {
|
|
72
|
-
const v =
|
|
98
|
+
const v = value instanceof Array ? value[idx] : idx === 0 ? value : undefined
|
|
73
99
|
|
|
74
100
|
switch (type) {
|
|
75
101
|
case 'select':
|
|
@@ -105,8 +131,8 @@ export class OxDataEntryForm extends LitElement {
|
|
|
105
131
|
})
|
|
106
132
|
|
|
107
133
|
return html` <label .title=${description}>
|
|
108
|
-
<div>${name}</div>
|
|
109
|
-
<div>${elements}</div>
|
|
134
|
+
<div name>${name}${unit ? `(${unit})` : ''}</div>
|
|
135
|
+
<div elements>${elements}</div>
|
|
110
136
|
</label>`
|
|
111
137
|
})
|
|
112
138
|
}
|
|
@@ -115,19 +141,14 @@ export class OxDataEntryForm extends LitElement {
|
|
|
115
141
|
const dataItems = this.dataSet!.dataItems
|
|
116
142
|
|
|
117
143
|
return (dataItems || []).reduce((sum, dataItem) => {
|
|
118
|
-
const { tag,
|
|
144
|
+
const { tag, type } = dataItem
|
|
119
145
|
|
|
120
146
|
const editors = Array.prototype.slice.call(
|
|
121
147
|
this.renderRoot.querySelectorAll(`[name=${tag}]`) as NodeListOf<HTMLInputElement>
|
|
122
148
|
) as HTMLInputElement[]
|
|
123
149
|
|
|
124
150
|
if (editors.length > 0) {
|
|
125
|
-
sum[tag] =
|
|
126
|
-
editors.length > 1
|
|
127
|
-
? editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
|
|
128
|
-
: type === 'boolean'
|
|
129
|
-
? editors[0].checked
|
|
130
|
-
: editors[0].value
|
|
151
|
+
sum[tag] = editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
|
|
131
152
|
}
|
|
132
153
|
|
|
133
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,171 @@
|
|
|
1
|
+
import '@operato/input/ox-input-file.js'
|
|
2
|
+
|
|
3
|
+
import { DataItem, DataSample, DataSet } from './types.js'
|
|
4
|
+
import { LitElement, css, html } from 'lit'
|
|
5
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
6
|
+
|
|
7
|
+
import { OxDataUseCase } from './ox-data-use-case.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
|
+
return html` <h2>${this.dataSample?.name}</h2>
|
|
96
|
+
<p page-description>
|
|
97
|
+
${this.dataSample?.description}<br /><br />
|
|
98
|
+
collected at: ${this.dataSample && formatter.format(new Date(this.dataSample?.collectedAt!))}
|
|
99
|
+
</p>
|
|
100
|
+
|
|
101
|
+
<form @change=${(e: Event) => this.onChange(e)}>
|
|
102
|
+
<table>
|
|
103
|
+
<tr>
|
|
104
|
+
<th item>item</th>
|
|
105
|
+
<th>description</th>
|
|
106
|
+
<th>unit</th>
|
|
107
|
+
<th value>value</th>
|
|
108
|
+
<th>specification</th>
|
|
109
|
+
<th>OOC</th>
|
|
110
|
+
<th>OOS</th>
|
|
111
|
+
</tr>
|
|
112
|
+
${dataItems.map(dataItem => {
|
|
113
|
+
const { ooc, oos } = this.evaluateOOC(dataItem, data?.[dataItem.tag]) || {}
|
|
114
|
+
return html`
|
|
115
|
+
<tr ?ooc=${ooc} ?oos=${oos}>
|
|
116
|
+
<td name>${dataItem.name}</td>
|
|
117
|
+
<td>${dataItem.description}</td>
|
|
118
|
+
<td>${dataItem.unit}</td>
|
|
119
|
+
<td>${this.buildValue(dataItem)}</td>
|
|
120
|
+
<td><pre>${this.buildSpec(dataItem)}</pre></td>
|
|
121
|
+
<td>${ooc ? html`<mwc-icon>done</mwc-icon>` : ''}</td>
|
|
122
|
+
<td>${oos ? html`<mwc-icon>done</mwc-icon>` : ''}</td>
|
|
123
|
+
</tr>
|
|
124
|
+
`
|
|
125
|
+
})}
|
|
126
|
+
</table>
|
|
127
|
+
</form>`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private onChange(e: Event) {}
|
|
131
|
+
|
|
132
|
+
private buildSpec(dataItem: DataItem) {
|
|
133
|
+
return OxDataUseCase.elaborateDataItemSpec(this.dataSet!, dataItem)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private buildValue(dataItem: DataItem) {
|
|
137
|
+
const { tag, type } = dataItem
|
|
138
|
+
|
|
139
|
+
if (!this.dataSample) {
|
|
140
|
+
return ''
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const value = this.dataSample?.data[dataItem.tag]
|
|
144
|
+
if (value === undefined) {
|
|
145
|
+
return ''
|
|
146
|
+
}
|
|
147
|
+
const values = value instanceof Array ? value : [value]
|
|
148
|
+
|
|
149
|
+
const elements = values.map((_: any, idx) => {
|
|
150
|
+
const v = value[idx]
|
|
151
|
+
|
|
152
|
+
switch (type) {
|
|
153
|
+
case 'boolean':
|
|
154
|
+
return html` <input type="checkbox" name=${tag} .checked=${v} disabled />`
|
|
155
|
+
break
|
|
156
|
+
|
|
157
|
+
case 'select':
|
|
158
|
+
case 'number':
|
|
159
|
+
case 'string':
|
|
160
|
+
default:
|
|
161
|
+
return String(v === undefined ? '' : v)
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return type === 'boolean' ? elements : elements.join(', ')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private evaluateOOC(dataItem: DataItem, value: any) {
|
|
169
|
+
return OxDataUseCase.evaluateItem(this.dataSet!, dataItem, value)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -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,70 @@
|
|
|
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
|
+
collectedAt: Date
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type DataOocState = 'CREATED' | 'REVIEWED' | 'CORRECTED'
|
|
55
|
+
|
|
56
|
+
export type DataOoc = DataSample & {
|
|
57
|
+
state: DataOocState
|
|
58
|
+
correctiveAction: string
|
|
59
|
+
history: {
|
|
60
|
+
user: {
|
|
61
|
+
id: string
|
|
62
|
+
name: string
|
|
63
|
+
}
|
|
64
|
+
state: DataOocState
|
|
65
|
+
comment: string
|
|
66
|
+
timestamp: number
|
|
67
|
+
}[]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type EvaluationResult = { oos: boolean; ooc: boolean }
|