@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.
Files changed (100) hide show
  1. package/CHANGELOG.md +379 -0
  2. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js +1 -1
  3. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js.map +1 -1
  4. package/dist/src/grist-editor/ox-popup-data-item-spec.js +6 -4
  5. package/dist/src/grist-editor/ox-popup-data-item-spec.js.map +1 -1
  6. package/dist/src/index.d.ts +1 -5
  7. package/dist/src/index.js +1 -5
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/ox-data-entry-form.js +24 -4
  10. package/dist/src/ox-data-entry-form.js.map +1 -1
  11. package/dist/src/ox-data-item-spec.d.ts +2 -2
  12. package/dist/src/ox-data-item-spec.js +71 -14
  13. package/dist/src/ox-data-item-spec.js.map +1 -1
  14. package/dist/src/ox-data-ooc-view.js +73 -14
  15. package/dist/src/ox-data-ooc-view.js.map +1 -1
  16. package/dist/src/ox-data-sample-view.d.ts +1 -3
  17. package/dist/src/ox-data-sample-view.js +40 -38
  18. package/dist/src/ox-data-sample-view.js.map +1 -1
  19. package/dist/src/types.d.ts +25 -6
  20. package/dist/src/types.js.map +1 -1
  21. package/dist/src/usecase/ccp/index.d.ts +1 -0
  22. package/dist/src/usecase/ccp/index.js +8 -0
  23. package/dist/src/usecase/ccp/index.js.map +1 -0
  24. package/dist/src/usecase/ccp/ox-data-use-case-ccp.d.ts +7 -0
  25. package/dist/src/usecase/ccp/ox-data-use-case-ccp.js +102 -0
  26. package/dist/src/usecase/ccp/ox-data-use-case-ccp.js.map +1 -0
  27. package/dist/src/usecase/ccp/ox-input-ccp-limits.d.ts +44 -0
  28. package/dist/src/usecase/ccp/ox-input-ccp-limits.js +171 -0
  29. package/dist/src/usecase/ccp/ox-input-ccp-limits.js.map +1 -0
  30. package/dist/src/usecase/ccp/ox-property-editor-ccp-limits.d.ts +5 -0
  31. package/dist/src/usecase/ccp/ox-property-editor-ccp-limits.js +24 -0
  32. package/dist/src/usecase/ccp/ox-property-editor-ccp-limits.js.map +1 -0
  33. package/dist/src/{ox-data-use-case.d.ts → usecase/ox-data-use-case.d.ts} +5 -5
  34. package/dist/src/{ox-data-use-case.js → usecase/ox-data-use-case.js} +22 -34
  35. package/dist/src/usecase/ox-data-use-case.js.map +1 -0
  36. package/dist/src/usecase/qc/index.d.ts +1 -0
  37. package/dist/src/usecase/qc/index.js +8 -0
  38. package/dist/src/usecase/qc/index.js.map +1 -0
  39. package/dist/src/usecase/qc/ox-data-use-case-qc.d.ts +7 -0
  40. package/dist/src/usecase/qc/ox-data-use-case-qc.js +55 -0
  41. package/dist/src/usecase/qc/ox-data-use-case-qc.js.map +1 -0
  42. package/dist/src/usecase/qc/ox-input-qc-limits.d.ts +44 -0
  43. package/dist/src/usecase/qc/ox-input-qc-limits.js +171 -0
  44. package/dist/src/usecase/qc/ox-input-qc-limits.js.map +1 -0
  45. package/dist/src/usecase/qc/ox-property-editor-qc-limits.d.ts +5 -0
  46. package/dist/src/usecase/qc/ox-property-editor-qc-limits.js +24 -0
  47. package/dist/src/usecase/qc/ox-property-editor-qc-limits.js.map +1 -0
  48. package/dist/stories/ox-data-entry-form.stories.d.ts +19 -0
  49. package/dist/stories/ox-data-entry-form.stories.js +152 -0
  50. package/dist/stories/ox-data-entry-form.stories.js.map +1 -0
  51. package/dist/stories/ox-data-item-spec.stories.d.ts +26 -0
  52. package/dist/stories/ox-data-item-spec.stories.js +102 -0
  53. package/dist/stories/ox-data-item-spec.stories.js.map +1 -0
  54. package/dist/stories/ox-data-ooc-view.stories.d.ts +19 -0
  55. package/dist/stories/ox-data-ooc-view.stories.js +242 -0
  56. package/dist/stories/ox-data-ooc-view.stories.js.map +1 -0
  57. package/dist/stories/ox-data-sample-view.stories.d.ts +19 -0
  58. package/dist/stories/ox-data-sample-view.stories.js +213 -0
  59. package/dist/stories/ox-data-sample-view.stories.js.map +1 -0
  60. package/dist/stories/ox-grist-editor-data-item-spec.stories.d.ts +27 -0
  61. package/dist/stories/ox-grist-editor-data-item-spec.stories.js +389 -0
  62. package/dist/stories/ox-grist-editor-data-item-spec.stories.js.map +1 -0
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +24 -18
  65. package/src/grist-editor/ox-grist-editor-data-item-spec.ts +1 -1
  66. package/src/grist-editor/ox-popup-data-item-spec.ts +8 -5
  67. package/src/index.ts +1 -5
  68. package/src/ox-data-entry-form.ts +24 -4
  69. package/src/ox-data-item-spec.ts +73 -16
  70. package/src/ox-data-ooc-view.ts +73 -14
  71. package/src/ox-data-sample-view.ts +43 -39
  72. package/src/types.ts +31 -6
  73. package/src/usecase/ccp/index.ts +10 -0
  74. package/src/usecase/ccp/ox-data-use-case-ccp.ts +147 -0
  75. package/src/usecase/ccp/ox-input-ccp-limits.ts +161 -0
  76. package/src/usecase/ccp/ox-property-editor-ccp-limits.ts +23 -0
  77. package/src/{ox-data-use-case.ts → usecase/ox-data-use-case.ts} +27 -41
  78. package/src/usecase/qc/index.ts +10 -0
  79. package/src/usecase/qc/ox-data-use-case-qc.ts +72 -0
  80. package/src/usecase/qc/ox-input-qc-limits.ts +161 -0
  81. package/src/usecase/qc/ox-property-editor-qc-limits.ts +23 -0
  82. package/stories/ox-data-entry-form.stories.ts +165 -0
  83. package/stories/ox-data-item-spec.stories.ts +121 -0
  84. package/stories/ox-data-ooc-view.stories.ts +256 -0
  85. package/stories/ox-data-sample-view.stories.ts +227 -0
  86. package/stories/ox-grist-editor-data-item-spec.stories.ts +409 -0
  87. package/translations/en.json +26 -8
  88. package/translations/ko.json +25 -7
  89. package/translations/ms.json +26 -8
  90. package/translations/zh.json +25 -7
  91. package/demo/index.html +0 -28
  92. package/demo/ox-data-entry-form.html +0 -118
  93. package/demo/ox-data-item-spec.html +0 -152
  94. package/demo/ox-data-ooc-view.html +0 -185
  95. package/demo/ox-data-sample-view.html +0 -150
  96. package/demo/ox-grist-editor-data-item-spec.html +0 -476
  97. package/dist/src/ox-data-sample-view copy.d.ts +0 -13
  98. package/dist/src/ox-data-sample-view copy.js +0 -214
  99. package/dist/src/ox-data-sample-view copy.js.map +0 -1
  100. 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, DataItemSpecSet, DataSet, EvaluationResult } from './types'
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(dataSet: DataSet, dataItem: DataItem): string {
23
- if (!dataSet.useCase) {
24
- return ''
25
- }
22
+ public static elaborateDataItemSpec(useCaseNames: string[], spec: DataSpecLimitSet): string {
23
+ var texts: string[] = []
26
24
 
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]
25
+ useCaseNames.forEach(useCaseName => {
26
+ const useCase = OxDataUseCase.getUseCase(useCaseName)
34
27
  if (!useCase) {
35
- continue
28
+ return
36
29
  }
37
30
 
38
- const specs = dataItem.spec?.[dataSet.useCase]
39
- if (!specs) {
40
- continue
31
+ const limits = spec?.[useCaseName]
32
+ if (!limits) {
33
+ return
41
34
  }
42
35
 
43
- const result = useCase.elaborateUseCaseSpec(dataItem.spec)
36
+ const result = useCase.elaborateUseCaseSpec(limits)
44
37
  if (!result) {
45
- continue
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 | undefined {
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?.evaluate(specs, values)
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(dataSet: DataSet, dataItem: DataItem, values: any | any[]): EvaluationResult | undefined {
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
- for (let j = 0; j < useCases.length; j++) {
126
- const useCase = useCases[j]
107
+ const useCaseNames = Object.keys(spec || {})
108
+
109
+ if (useCaseNames.length === 0) {
110
+ return { ooc, oos }
111
+ }
127
112
 
128
- const specs = dataItem.spec?.[dataSet.useCase]
129
- if (!specs) {
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(specs, values)
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): DataItemSpecSet
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
+ }