@operato/dataset 1.0.0-beta.7 → 1.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.
- package/CHANGELOG.md +379 -0
- package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js +1 -1
- package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js.map +1 -1
- package/dist/src/grist-editor/ox-popup-data-item-spec.js +6 -4
- package/dist/src/grist-editor/ox-popup-data-item-spec.js.map +1 -1
- package/dist/src/index.d.ts +1 -5
- package/dist/src/index.js +1 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/ox-data-entry-form.js +24 -4
- package/dist/src/ox-data-entry-form.js.map +1 -1
- package/dist/src/ox-data-item-spec.d.ts +2 -2
- package/dist/src/ox-data-item-spec.js +71 -14
- package/dist/src/ox-data-item-spec.js.map +1 -1
- package/dist/src/ox-data-ooc-view.js +73 -14
- package/dist/src/ox-data-ooc-view.js.map +1 -1
- package/dist/src/ox-data-sample-view.d.ts +1 -3
- package/dist/src/ox-data-sample-view.js +40 -38
- package/dist/src/ox-data-sample-view.js.map +1 -1
- package/dist/src/types.d.ts +25 -6
- package/dist/src/types.js.map +1 -1
- package/dist/src/usecase/ccp/index.d.ts +1 -0
- package/dist/src/usecase/ccp/index.js +8 -0
- package/dist/src/usecase/ccp/index.js.map +1 -0
- package/dist/src/usecase/ccp/ox-data-use-case-ccp.d.ts +7 -0
- package/dist/src/usecase/ccp/ox-data-use-case-ccp.js +102 -0
- package/dist/src/usecase/ccp/ox-data-use-case-ccp.js.map +1 -0
- package/dist/src/usecase/ccp/ox-input-ccp-limits.d.ts +44 -0
- package/dist/src/usecase/ccp/ox-input-ccp-limits.js +171 -0
- package/dist/src/usecase/ccp/ox-input-ccp-limits.js.map +1 -0
- package/dist/src/usecase/ccp/ox-property-editor-ccp-limits.d.ts +5 -0
- package/dist/src/usecase/ccp/ox-property-editor-ccp-limits.js +24 -0
- package/dist/src/usecase/ccp/ox-property-editor-ccp-limits.js.map +1 -0
- package/dist/src/{ox-data-use-case.d.ts → usecase/ox-data-use-case.d.ts} +5 -5
- package/dist/src/{ox-data-use-case.js → usecase/ox-data-use-case.js} +22 -34
- package/dist/src/usecase/ox-data-use-case.js.map +1 -0
- package/dist/src/usecase/qc/index.d.ts +1 -0
- package/dist/src/usecase/qc/index.js +8 -0
- package/dist/src/usecase/qc/index.js.map +1 -0
- package/dist/src/usecase/qc/ox-data-use-case-qc.d.ts +7 -0
- package/dist/src/usecase/qc/ox-data-use-case-qc.js +55 -0
- package/dist/src/usecase/qc/ox-data-use-case-qc.js.map +1 -0
- package/dist/src/usecase/qc/ox-input-qc-limits.d.ts +44 -0
- package/dist/src/usecase/qc/ox-input-qc-limits.js +171 -0
- package/dist/src/usecase/qc/ox-input-qc-limits.js.map +1 -0
- package/dist/src/usecase/qc/ox-property-editor-qc-limits.d.ts +5 -0
- package/dist/src/usecase/qc/ox-property-editor-qc-limits.js +24 -0
- package/dist/src/usecase/qc/ox-property-editor-qc-limits.js.map +1 -0
- package/dist/stories/ox-data-entry-form.stories.d.ts +19 -0
- package/dist/stories/ox-data-entry-form.stories.js +152 -0
- package/dist/stories/ox-data-entry-form.stories.js.map +1 -0
- package/dist/stories/ox-data-item-spec.stories.d.ts +26 -0
- package/dist/stories/ox-data-item-spec.stories.js +102 -0
- package/dist/stories/ox-data-item-spec.stories.js.map +1 -0
- package/dist/stories/ox-data-ooc-view.stories.d.ts +19 -0
- package/dist/stories/ox-data-ooc-view.stories.js +242 -0
- package/dist/stories/ox-data-ooc-view.stories.js.map +1 -0
- package/dist/stories/ox-data-sample-view.stories.d.ts +19 -0
- package/dist/stories/ox-data-sample-view.stories.js +213 -0
- package/dist/stories/ox-data-sample-view.stories.js.map +1 -0
- package/dist/stories/ox-grist-editor-data-item-spec.stories.d.ts +27 -0
- package/dist/stories/ox-grist-editor-data-item-spec.stories.js +389 -0
- package/dist/stories/ox-grist-editor-data-item-spec.stories.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -18
- package/src/grist-editor/ox-grist-editor-data-item-spec.ts +1 -1
- package/src/grist-editor/ox-popup-data-item-spec.ts +8 -5
- package/src/index.ts +1 -5
- package/src/ox-data-entry-form.ts +24 -4
- package/src/ox-data-item-spec.ts +73 -16
- package/src/ox-data-ooc-view.ts +73 -14
- package/src/ox-data-sample-view.ts +43 -39
- package/src/types.ts +31 -6
- package/src/usecase/ccp/index.ts +10 -0
- package/src/usecase/ccp/ox-data-use-case-ccp.ts +147 -0
- package/src/usecase/ccp/ox-input-ccp-limits.ts +161 -0
- package/src/usecase/ccp/ox-property-editor-ccp-limits.ts +23 -0
- package/src/{ox-data-use-case.ts → usecase/ox-data-use-case.ts} +27 -41
- package/src/usecase/qc/index.ts +10 -0
- package/src/usecase/qc/ox-data-use-case-qc.ts +72 -0
- package/src/usecase/qc/ox-input-qc-limits.ts +161 -0
- package/src/usecase/qc/ox-property-editor-qc-limits.ts +23 -0
- package/stories/ox-data-entry-form.stories.ts +165 -0
- package/stories/ox-data-item-spec.stories.ts +121 -0
- package/stories/ox-data-ooc-view.stories.ts +256 -0
- package/stories/ox-data-sample-view.stories.ts +227 -0
- package/stories/ox-grist-editor-data-item-spec.stories.ts +409 -0
- package/translations/en.json +26 -8
- package/translations/ko.json +25 -7
- package/translations/ms.json +26 -8
- package/translations/zh.json +25 -7
- package/demo/index.html +0 -28
- package/demo/ox-data-entry-form.html +0 -118
- package/demo/ox-data-item-spec.html +0 -152
- package/demo/ox-data-ooc-view.html +0 -185
- package/demo/ox-data-sample-view.html +0 -150
- package/demo/ox-grist-editor-data-item-spec.html +0 -476
- package/dist/src/ox-data-sample-view copy.d.ts +0 -13
- package/dist/src/ox-data-sample-view copy.js +0 -214
- package/dist/src/ox-data-sample-view copy.js.map +0 -1
- package/dist/src/ox-data-use-case.js.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import i18next from 'i18next' // Since this is a common module, @operato/i18n was deliberately avoided.
|
|
2
|
+
|
|
3
|
+
import { DataItem, EvaluationResult, UseCaseDefinition } from '../../types'
|
|
4
|
+
import { OxDataUseCase } from '../ox-data-use-case.js'
|
|
5
|
+
|
|
6
|
+
export class OxDataUseCaseCCP extends OxDataUseCase {
|
|
7
|
+
getSpecification(dataItem: DataItem): UseCaseDefinition {
|
|
8
|
+
return {
|
|
9
|
+
name: 'CCP',
|
|
10
|
+
description: 'Critical Control Point Data Spec',
|
|
11
|
+
help: '',
|
|
12
|
+
specs: [
|
|
13
|
+
{
|
|
14
|
+
type: 'ccp-limits' /* A value which seperates acceptability from unacceptability */,
|
|
15
|
+
label: 'critical-limits',
|
|
16
|
+
name: 'criticalLimits',
|
|
17
|
+
property: dataItem
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'ccp-limits',
|
|
21
|
+
label: 'target-limits',
|
|
22
|
+
name: 'targetLimits',
|
|
23
|
+
property: dataItem
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
evaluate(spec: any, values: any | any[]): EvaluationResult | undefined {
|
|
30
|
+
if (!spec || typeof values === 'undefined') {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!(values instanceof Array)) {
|
|
35
|
+
values = [values]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
minimum: criticalMinimum,
|
|
40
|
+
maximum: criticalMaximum,
|
|
41
|
+
acceptables: criticalAcceptables
|
|
42
|
+
} = spec['criticalLimits'] || {}
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
minimum: targetMinimum,
|
|
46
|
+
maximum: targetMaximum,
|
|
47
|
+
acceptables: targetAcceptables
|
|
48
|
+
} = spec['targetLimits'] || {}
|
|
49
|
+
|
|
50
|
+
var oos = false
|
|
51
|
+
var ooc = false
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < values.length; i++) {
|
|
54
|
+
const value = values[i]
|
|
55
|
+
|
|
56
|
+
if (criticalMinimum != null && value < criticalMinimum) {
|
|
57
|
+
oos = true
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (criticalMaximum != null && value > criticalMaximum) {
|
|
62
|
+
oos = true
|
|
63
|
+
break
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (criticalAcceptables != null && !criticalAcceptables.includes(value)) {
|
|
67
|
+
oos = true
|
|
68
|
+
break
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < values.length; i++) {
|
|
73
|
+
const value = values[i]
|
|
74
|
+
|
|
75
|
+
if (targetMinimum != null && value < targetMinimum) {
|
|
76
|
+
ooc = true
|
|
77
|
+
break
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (targetMaximum != null && value > targetMaximum) {
|
|
81
|
+
ooc = true
|
|
82
|
+
break
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (targetAcceptables != null && !targetAcceptables.includes(value)) {
|
|
86
|
+
ooc = true
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { oos, ooc }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
elaborateUseCaseSpec(limits: any): string | undefined {
|
|
95
|
+
if (!limits) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var text = ''
|
|
100
|
+
|
|
101
|
+
const {
|
|
102
|
+
minimum: criticalMinimum,
|
|
103
|
+
maximum: criticalMaximum,
|
|
104
|
+
acceptables: criticalAcceptables
|
|
105
|
+
} = limits['criticalLimits'] || {}
|
|
106
|
+
|
|
107
|
+
if (criticalMinimum != null || criticalMaximum != null || criticalAcceptables != null) {
|
|
108
|
+
text += `${i18next.t('label.critical-limits')}\n`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (criticalMinimum != null) {
|
|
112
|
+
text += `\t${i18next.t('label.minimum value')} : ${criticalMinimum}\n`
|
|
113
|
+
}
|
|
114
|
+
if (criticalMaximum != null) {
|
|
115
|
+
text += `\t${i18next.t('label.maximum value')} : ${criticalMaximum}\n`
|
|
116
|
+
}
|
|
117
|
+
if (criticalAcceptables != null) {
|
|
118
|
+
text += `\t${i18next.t('label.acceptables')} : ${
|
|
119
|
+
criticalAcceptables instanceof Array ? criticalAcceptables.join(', ') : criticalAcceptables
|
|
120
|
+
}\n`
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const {
|
|
124
|
+
minimum: targetMinimum,
|
|
125
|
+
maximum: targetMaximum,
|
|
126
|
+
acceptables: targetAcceptables
|
|
127
|
+
} = limits['targetLimits'] || {}
|
|
128
|
+
|
|
129
|
+
if (targetMinimum != null || targetMaximum != null || targetAcceptables != null) {
|
|
130
|
+
text += `${i18next.t('label.target-limits')}\n`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (targetMinimum != null) {
|
|
134
|
+
text += `\t${i18next.t('label.minimum value')} : ${targetMinimum}\n`
|
|
135
|
+
}
|
|
136
|
+
if (targetMaximum != null) {
|
|
137
|
+
text += `\t${i18next.t('label.maximum value')} : ${targetMaximum}\n`
|
|
138
|
+
}
|
|
139
|
+
if (targetAcceptables != null) {
|
|
140
|
+
text += `\t${i18next.t('label.acceptables')} : ${
|
|
141
|
+
targetAcceptables instanceof Array ? targetAcceptables.join(', ') : targetAcceptables
|
|
142
|
+
}\n`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return text
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { css, html, LitElement } from 'lit'
|
|
6
|
+
import { customElement, property, query, queryAll } from 'lit/decorators.js'
|
|
7
|
+
|
|
8
|
+
import { i18next } from '@operato/i18n'
|
|
9
|
+
|
|
10
|
+
export enum DataItemType {
|
|
11
|
+
number = 'number',
|
|
12
|
+
text = 'text',
|
|
13
|
+
boolean = 'boolean',
|
|
14
|
+
select = 'select',
|
|
15
|
+
file = 'file'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type CcpLimitValue = {
|
|
19
|
+
minimum?: number
|
|
20
|
+
maximum?: number
|
|
21
|
+
acceptables?: string[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
element for CCP limits
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
|
|
29
|
+
<ox-input-ccp-limits
|
|
30
|
+
.value=${value}
|
|
31
|
+
.type=${type}>
|
|
32
|
+
</ox-input-ccp-limits>
|
|
33
|
+
*/
|
|
34
|
+
@customElement('ox-input-ccp-limits')
|
|
35
|
+
export class OxInputCcpLimits extends LitElement {
|
|
36
|
+
static styles = css`
|
|
37
|
+
:host {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-content: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
label {
|
|
44
|
+
margin: 0 var(--margin-default) var(--margin-default) 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
label div > * {
|
|
48
|
+
vertical-align: middle;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
button {
|
|
52
|
+
width: 20px;
|
|
53
|
+
text-align: center;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
div[name],
|
|
57
|
+
input[type='checkbox'] + span {
|
|
58
|
+
display: inline-block;
|
|
59
|
+
font: var(--label-font);
|
|
60
|
+
color: var(--label-color);
|
|
61
|
+
text-align: right;
|
|
62
|
+
}
|
|
63
|
+
input,
|
|
64
|
+
select {
|
|
65
|
+
border: var(--input-field-border);
|
|
66
|
+
border-radius: var(--input-field-border-radius);
|
|
67
|
+
padding: var(--input-field-padding);
|
|
68
|
+
font: var(--input-field-font);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
input[type='checkbox'] {
|
|
72
|
+
width: initial;
|
|
73
|
+
}
|
|
74
|
+
input[checked] + span {
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
}
|
|
77
|
+
[unit],
|
|
78
|
+
[value] {
|
|
79
|
+
display: inline-block;
|
|
80
|
+
opacity: 0.7;
|
|
81
|
+
font: var(--form-sublabel-font);
|
|
82
|
+
}
|
|
83
|
+
[properties] {
|
|
84
|
+
font: var(--input-field-font);
|
|
85
|
+
color: var(--label-color);
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
|
|
89
|
+
@property({ type: Object }) value: CcpLimitValue = {}
|
|
90
|
+
@property({ type: String }) type: DataItemType = DataItemType.number
|
|
91
|
+
@property({ type: String }) unit?: string
|
|
92
|
+
@property({ type: Array }) options: { options?: { text: string; value: string }[] } = {}
|
|
93
|
+
|
|
94
|
+
@query('[name=minimum]') minimum!: HTMLInputElement
|
|
95
|
+
@query('[name=maximum]') maximum!: HTMLInputElement
|
|
96
|
+
@queryAll('[type=checkbox]:checked') checkedAll!: NodeListOf<HTMLInputElement>
|
|
97
|
+
|
|
98
|
+
firstUpdated() {
|
|
99
|
+
this.renderRoot.addEventListener('change', this.onChange.bind(this))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render() {
|
|
103
|
+
const { minimum, maximum, acceptables = [] } = this.value || {}
|
|
104
|
+
|
|
105
|
+
return html`
|
|
106
|
+
<div>
|
|
107
|
+
${this.type === DataItemType.number
|
|
108
|
+
? html`
|
|
109
|
+
<label>
|
|
110
|
+
<div name>${i18next.t('text.minimum value')}</div>
|
|
111
|
+
<input type="number" name="minimum" .value=${minimum} />
|
|
112
|
+
<div unit>${this.unit}</div>
|
|
113
|
+
</select>
|
|
114
|
+
</label>
|
|
115
|
+
<label>
|
|
116
|
+
<div name>${i18next.t('text.maximum value')}</div>
|
|
117
|
+
<input type="number" name="maximum" .value=${maximum} />
|
|
118
|
+
<div unit>${this.unit}</div>
|
|
119
|
+
</label>
|
|
120
|
+
</div>
|
|
121
|
+
`
|
|
122
|
+
: this.type === DataItemType.select
|
|
123
|
+
? html`
|
|
124
|
+
<!-- <div>Acceptables</div> -->
|
|
125
|
+
${this.options?.options?.map(
|
|
126
|
+
option => html`
|
|
127
|
+
<div>
|
|
128
|
+
<input type="checkbox" data-value=${option.value} ?checked=${acceptables.includes(option.value)} />
|
|
129
|
+
<span>${option.text}</span>
|
|
130
|
+
<span value>(${option.value})</span>
|
|
131
|
+
</div>
|
|
132
|
+
`
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
`
|
|
136
|
+
: html` <div properties>${i18next.t('text.no properties to set')}</div> `}
|
|
137
|
+
</div>
|
|
138
|
+
`
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private onChange(e: Event) {
|
|
142
|
+
if (this.type === DataItemType.number) {
|
|
143
|
+
this.value = {
|
|
144
|
+
minimum: this.minimum.valueAsNumber,
|
|
145
|
+
maximum: this.maximum.valueAsNumber
|
|
146
|
+
}
|
|
147
|
+
} else if (this.type === DataItemType.select) {
|
|
148
|
+
this.value = {
|
|
149
|
+
acceptables: Array.from(this.checkedAll).map(checked => {
|
|
150
|
+
const x = checked.getAttribute('data-value')!
|
|
151
|
+
console.log(x)
|
|
152
|
+
return x
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
this.value = {}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import './ox-input-ccp-limits.js'
|
|
2
|
+
|
|
3
|
+
import { html } from 'lit'
|
|
4
|
+
import { customElement } from 'lit/decorators.js'
|
|
5
|
+
|
|
6
|
+
import { OxPropertyEditor, PropertySpec } from '@operato/property-editor'
|
|
7
|
+
|
|
8
|
+
@customElement('ox-property-editor-ccp-limits')
|
|
9
|
+
export class OxPropertyEditorCcpLimits extends OxPropertyEditor {
|
|
10
|
+
editorTemplate(value: any, spec: PropertySpec) {
|
|
11
|
+
const { type, options, unit } = spec.property || {}
|
|
12
|
+
|
|
13
|
+
return html`
|
|
14
|
+
<ox-input-ccp-limits
|
|
15
|
+
id="editor"
|
|
16
|
+
.value=${value}
|
|
17
|
+
.type=${type}
|
|
18
|
+
.unit=${unit}
|
|
19
|
+
.options=${options}
|
|
20
|
+
></ox-input-ccp-limits>
|
|
21
|
+
`
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataItem,
|
|
1
|
+
import { DataItem, DataSet, DataSpecLimitSet, EvaluationResult, UseCaseDefinition } from '../types'
|
|
2
2
|
|
|
3
3
|
export abstract class OxDataUseCase {
|
|
4
4
|
static registry: { [name: string]: OxDataUseCase } = {}
|
|
@@ -19,39 +19,33 @@ export abstract class OxDataUseCase {
|
|
|
19
19
|
return OxDataUseCase.registry[name]
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
public static elaborateDataItemSpec(
|
|
23
|
-
|
|
24
|
-
return ''
|
|
25
|
-
}
|
|
22
|
+
public static elaborateDataItemSpec(useCaseNames: string[], spec: DataSpecLimitSet): string {
|
|
23
|
+
var texts: string[] = []
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
var texts = []
|
|
31
|
-
|
|
32
|
-
for (let j = 0; j < useCases.length; j++) {
|
|
33
|
-
const useCase = useCases[j]
|
|
25
|
+
useCaseNames.forEach(useCaseName => {
|
|
26
|
+
const useCase = OxDataUseCase.getUseCase(useCaseName)
|
|
34
27
|
if (!useCase) {
|
|
35
|
-
|
|
28
|
+
return
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
const
|
|
39
|
-
if (!
|
|
40
|
-
|
|
31
|
+
const limits = spec?.[useCaseName]
|
|
32
|
+
if (!limits) {
|
|
33
|
+
return
|
|
41
34
|
}
|
|
42
35
|
|
|
43
|
-
const result = useCase.elaborateUseCaseSpec(
|
|
36
|
+
const result = useCase.elaborateUseCaseSpec(limits)
|
|
44
37
|
if (!result) {
|
|
45
|
-
|
|
38
|
+
return
|
|
46
39
|
}
|
|
47
40
|
|
|
41
|
+
texts.push(`[ ${useCaseName} ]`)
|
|
48
42
|
texts.push(result)
|
|
49
|
-
}
|
|
43
|
+
})
|
|
50
44
|
|
|
51
45
|
return texts.join('\n')
|
|
52
46
|
}
|
|
53
47
|
|
|
54
|
-
public static evaluate(dataSet: DataSet, dataItems: DataItem[], data: any): EvaluationResult
|
|
48
|
+
public static evaluate(dataSet: DataSet, dataItems: DataItem[], data: any): EvaluationResult {
|
|
55
49
|
var ooc = false
|
|
56
50
|
var oos = false
|
|
57
51
|
|
|
@@ -86,7 +80,7 @@ export abstract class OxDataUseCase {
|
|
|
86
80
|
continue
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
const result = useCase
|
|
83
|
+
const result = useCase!.evaluate(specs, values)
|
|
90
84
|
|
|
91
85
|
if (result) {
|
|
92
86
|
ooc ||= result.ooc
|
|
@@ -98,22 +92,10 @@ export abstract class OxDataUseCase {
|
|
|
98
92
|
return { ooc, oos }
|
|
99
93
|
}
|
|
100
94
|
|
|
101
|
-
public static evaluateItem(
|
|
95
|
+
public static evaluateItem(spec: DataSpecLimitSet, values: any | any[]): EvaluationResult | undefined {
|
|
102
96
|
var ooc = false
|
|
103
97
|
var oos = false
|
|
104
98
|
|
|
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
99
|
if (typeof values === 'undefined') {
|
|
118
100
|
return // TODO what if in case no value ?
|
|
119
101
|
}
|
|
@@ -122,26 +104,30 @@ export abstract class OxDataUseCase {
|
|
|
122
104
|
values = [values]
|
|
123
105
|
}
|
|
124
106
|
|
|
125
|
-
|
|
126
|
-
|
|
107
|
+
const useCaseNames = Object.keys(spec || {})
|
|
108
|
+
|
|
109
|
+
if (useCaseNames.length === 0) {
|
|
110
|
+
return { ooc, oos }
|
|
111
|
+
}
|
|
127
112
|
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
useCaseNames.forEach(useCaseName => {
|
|
114
|
+
const useCase = OxDataUseCase.getUseCase(useCaseName)
|
|
115
|
+
if (!useCase) {
|
|
130
116
|
return
|
|
131
117
|
}
|
|
132
118
|
|
|
133
|
-
const result = useCase?.evaluate(
|
|
119
|
+
const result = useCase?.evaluate(spec[useCaseName], values)
|
|
134
120
|
|
|
135
121
|
if (result) {
|
|
136
122
|
ooc ||= result.ooc
|
|
137
123
|
oos ||= result.oos
|
|
138
124
|
}
|
|
139
|
-
}
|
|
125
|
+
})
|
|
140
126
|
|
|
141
127
|
return { ooc, oos }
|
|
142
128
|
}
|
|
143
129
|
|
|
144
130
|
public abstract evaluate(specs: any, values: any[]): EvaluationResult | undefined
|
|
145
131
|
public abstract elaborateUseCaseSpec(spec: any): string | undefined
|
|
146
|
-
public abstract getSpecification(dataItem: DataItem):
|
|
132
|
+
public abstract getSpecification(dataItem: DataItem): UseCaseDefinition
|
|
147
133
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Here, only the common module of client and server is imported/exported. Be careful not to use a module dedicated to client or server. */
|
|
2
|
+
|
|
3
|
+
import { OxDataUseCase } from '../ox-data-use-case.js'
|
|
4
|
+
import { OxDataUseCaseQC } from './ox-data-use-case-qc.js'
|
|
5
|
+
|
|
6
|
+
// OxPropertyEditor.register({
|
|
7
|
+
// 'qc-limits': 'ox-property-editor-qc-limits'
|
|
8
|
+
// })
|
|
9
|
+
|
|
10
|
+
OxDataUseCase.registerUseCase('QC', new OxDataUseCaseQC())
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import i18next from 'i18next' // Since this is a common module, @operato/i18n was deliberately avoided.
|
|
2
|
+
|
|
3
|
+
import { DataItem, EvaluationResult, UseCaseDefinition } from '../../types.js'
|
|
4
|
+
import { OxDataUseCase } from '../ox-data-use-case.js'
|
|
5
|
+
|
|
6
|
+
export class OxDataUseCaseQC implements OxDataUseCase {
|
|
7
|
+
getSpecification(dataItem: DataItem): UseCaseDefinition {
|
|
8
|
+
return {
|
|
9
|
+
name: 'QC',
|
|
10
|
+
description: 'Quality Control Data Spec',
|
|
11
|
+
help: '',
|
|
12
|
+
specs: [
|
|
13
|
+
{
|
|
14
|
+
type: 'qc-limits' /* 'A value which seperates acceptability from unacceptability' */,
|
|
15
|
+
label: 'pass-limits',
|
|
16
|
+
name: 'passLimits',
|
|
17
|
+
property: dataItem
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
evaluate(spec: any, values: any[]): EvaluationResult {
|
|
24
|
+
const { minimum, maximum, acceptables } = spec['passLimits']
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < values.length; i++) {
|
|
27
|
+
const value = values[i]
|
|
28
|
+
|
|
29
|
+
if (minimum != null && value < minimum) {
|
|
30
|
+
return { oos: true, ooc: true }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (maximum != null && value > maximum) {
|
|
34
|
+
return { oos: true, ooc: true }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (acceptables != null && !acceptables.includes(value)) {
|
|
38
|
+
return { oos: true, ooc: true }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { oos: false, ooc: false }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
elaborateUseCaseSpec(limits: any): string | undefined {
|
|
46
|
+
if (!limits) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var text = ''
|
|
51
|
+
|
|
52
|
+
const { minimum, maximum, acceptables } = limits['passLimits'] || {}
|
|
53
|
+
|
|
54
|
+
if (minimum != null || maximum != null || acceptables != null) {
|
|
55
|
+
text += `${i18next.t('label.pass-limits')}\n`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (minimum != null) {
|
|
59
|
+
text += `\t${i18next.t('label.minimum value')} : ${minimum}\n`
|
|
60
|
+
}
|
|
61
|
+
if (maximum != null) {
|
|
62
|
+
text += `\t${i18next.t('label.maximum value')} : ${maximum}\n`
|
|
63
|
+
}
|
|
64
|
+
if (acceptables != null) {
|
|
65
|
+
text += `\t${i18next.t('label.acceptables')} : ${
|
|
66
|
+
acceptables instanceof Array ? acceptables.join(', ') : acceptables
|
|
67
|
+
}\n`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return text
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { css, html, LitElement } from 'lit'
|
|
6
|
+
import { customElement, property, query, queryAll } from 'lit/decorators.js'
|
|
7
|
+
|
|
8
|
+
import { i18next } from '@operato/i18n'
|
|
9
|
+
|
|
10
|
+
export enum DataItemType {
|
|
11
|
+
number = 'number',
|
|
12
|
+
text = 'text',
|
|
13
|
+
boolean = 'boolean',
|
|
14
|
+
select = 'select',
|
|
15
|
+
file = 'file'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type QcLimitValue = {
|
|
19
|
+
minimum?: number
|
|
20
|
+
maximum?: number
|
|
21
|
+
acceptables?: string[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
element for QC limits
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
|
|
29
|
+
<ox-input-qc-limits
|
|
30
|
+
.value=${value}
|
|
31
|
+
.type=${type}>
|
|
32
|
+
</ox-input-qc-limits>
|
|
33
|
+
*/
|
|
34
|
+
@customElement('ox-input-qc-limits')
|
|
35
|
+
export class OxInputQcLimits extends LitElement {
|
|
36
|
+
static styles = css`
|
|
37
|
+
:host {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-content: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
label {
|
|
44
|
+
margin: 0 var(--margin-default) var(--margin-default) 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
label div > * {
|
|
48
|
+
vertical-align: middle;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
button {
|
|
52
|
+
width: 20px;
|
|
53
|
+
text-align: center;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
div[name],
|
|
57
|
+
input[type='checkbox'] + span {
|
|
58
|
+
display: inline-block;
|
|
59
|
+
font: var(--label-font);
|
|
60
|
+
color: var(--label-color);
|
|
61
|
+
text-align: right;
|
|
62
|
+
}
|
|
63
|
+
input,
|
|
64
|
+
select {
|
|
65
|
+
border: var(--input-field-border);
|
|
66
|
+
border-radius: var(--input-field-border-radius);
|
|
67
|
+
padding: var(--input-field-padding);
|
|
68
|
+
font: var(--input-field-font);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
input[type='checkbox'] {
|
|
72
|
+
width: initial;
|
|
73
|
+
}
|
|
74
|
+
input[checked] + span {
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
}
|
|
77
|
+
[unit],
|
|
78
|
+
[value] {
|
|
79
|
+
display: inline-block;
|
|
80
|
+
opacity: 0.7;
|
|
81
|
+
font: var(--form-sublabel-font);
|
|
82
|
+
}
|
|
83
|
+
[properties] {
|
|
84
|
+
font: var(--input-field-font);
|
|
85
|
+
color: var(--label-color);
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
|
|
89
|
+
@property({ type: Object }) value: QcLimitValue = {}
|
|
90
|
+
@property({ type: String }) type: DataItemType = DataItemType.number
|
|
91
|
+
@property({ type: String }) unit?: string
|
|
92
|
+
@property({ type: Array }) options: { options?: { text: string; value: string }[] } = {}
|
|
93
|
+
|
|
94
|
+
@query('[name=minimum]') minimum!: HTMLInputElement
|
|
95
|
+
@query('[name=maximum]') maximum!: HTMLInputElement
|
|
96
|
+
@queryAll('[type=checkbox]:checked') checkedAll!: NodeListOf<HTMLInputElement>
|
|
97
|
+
|
|
98
|
+
firstUpdated() {
|
|
99
|
+
this.renderRoot.addEventListener('change', this.onChange.bind(this))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render() {
|
|
103
|
+
const { minimum, maximum, acceptables = [] } = this.value || {}
|
|
104
|
+
|
|
105
|
+
return html`
|
|
106
|
+
<div>
|
|
107
|
+
${this.type === DataItemType.number
|
|
108
|
+
? html`
|
|
109
|
+
<label>
|
|
110
|
+
<div name>${i18next.t('text.minimum value')}</div>
|
|
111
|
+
<input type="number" name="minimum" .value=${minimum} />
|
|
112
|
+
<div unit>${this.unit}</div>
|
|
113
|
+
</select>
|
|
114
|
+
</label>
|
|
115
|
+
<label>
|
|
116
|
+
<div name>${i18next.t('text.maximum value')}</div>
|
|
117
|
+
<input type="number" name="maximum" .value=${maximum} />
|
|
118
|
+
<div unit>${this.unit}</div>
|
|
119
|
+
</label>
|
|
120
|
+
</div>
|
|
121
|
+
`
|
|
122
|
+
: this.type === DataItemType.select
|
|
123
|
+
? html`
|
|
124
|
+
<!-- <div>Acceptables</div> -->
|
|
125
|
+
${this.options?.options?.map(
|
|
126
|
+
option => html`
|
|
127
|
+
<div>
|
|
128
|
+
<input type="checkbox" data-value=${option.value} ?checked=${acceptables.includes(option.value)} />
|
|
129
|
+
<span>${option.text}</span>
|
|
130
|
+
<span value>(${option.value})</span>
|
|
131
|
+
</div>
|
|
132
|
+
`
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
`
|
|
136
|
+
: html` <div properties>${i18next.t('text.no properties to set')}</div> `}
|
|
137
|
+
</div>
|
|
138
|
+
`
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private onChange(e: Event) {
|
|
142
|
+
if (this.type === DataItemType.number) {
|
|
143
|
+
this.value = {
|
|
144
|
+
minimum: this.minimum.valueAsNumber,
|
|
145
|
+
maximum: this.maximum.valueAsNumber
|
|
146
|
+
}
|
|
147
|
+
} else if (this.type === DataItemType.select) {
|
|
148
|
+
this.value = {
|
|
149
|
+
acceptables: Array.from(this.checkedAll).map(checked => {
|
|
150
|
+
const x = checked.getAttribute('data-value')!
|
|
151
|
+
console.log(x)
|
|
152
|
+
return x
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
this.value = {}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
|
|
160
|
+
}
|
|
161
|
+
}
|