@operato/dataset 1.0.0-alpha.36 → 1.0.0-alpha.39

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 (40) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/demo/index.html +2 -0
  3. package/demo/ox-data-item-spec.html +43 -39
  4. package/demo/ox-data-ooc-view.html +184 -0
  5. package/demo/ox-data-sample-view.html +150 -0
  6. package/demo/ox-grist-editor-data-item-spec.html +43 -39
  7. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js +5 -5
  8. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js.map +1 -1
  9. package/dist/src/index.d.ts +3 -0
  10. package/dist/src/index.js +3 -0
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/ox-data-entry-form.js +4 -9
  13. package/dist/src/ox-data-entry-form.js.map +1 -1
  14. package/dist/src/ox-data-item-spec.d.ts +1 -7
  15. package/dist/src/ox-data-item-spec.js +5 -15
  16. package/dist/src/ox-data-item-spec.js.map +1 -1
  17. package/dist/src/ox-data-ooc-view.d.ts +10 -0
  18. package/dist/src/ox-data-ooc-view.js +58 -0
  19. package/dist/src/ox-data-ooc-view.js.map +1 -0
  20. package/dist/src/ox-data-sample-view copy.d.ts +13 -0
  21. package/dist/src/ox-data-sample-view copy.js +214 -0
  22. package/dist/src/ox-data-sample-view copy.js.map +1 -0
  23. package/dist/src/ox-data-sample-view.d.ts +13 -0
  24. package/dist/src/ox-data-sample-view.js +165 -0
  25. package/dist/src/ox-data-sample-view.js.map +1 -0
  26. package/dist/src/ox-data-use-case.d.ts +16 -0
  27. package/dist/src/ox-data-use-case.js +111 -0
  28. package/dist/src/ox-data-use-case.js.map +1 -0
  29. package/dist/src/types.d.ts +36 -2
  30. package/dist/src/types.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +14 -11
  33. package/src/grist-editor/ox-grist-editor-data-item-spec.ts +5 -5
  34. package/src/index.ts +3 -0
  35. package/src/ox-data-entry-form.ts +4 -9
  36. package/src/ox-data-item-spec.ts +5 -17
  37. package/src/ox-data-ooc-view.ts +56 -0
  38. package/src/ox-data-sample-view.ts +171 -0
  39. package/src/ox-data-use-case.ts +147 -0
  40. package/src/types.ts +30 -2
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export * from './ox-data-entry-form.js'
2
+ export * from './ox-data-sample-view.js'
3
+ export * from './ox-data-ooc-view.js'
2
4
  export * from './ox-data-item-spec.js'
5
+ export * from './ox-data-use-case'
3
6
  export * from './types.js'
@@ -1,6 +1,6 @@
1
1
  import '@operato/input/ox-input-file.js'
2
2
 
3
- import { LitElement, css, html } from 'lit'
3
+ import { css, html, LitElement } from 'lit'
4
4
  import { customElement, property } from 'lit/decorators.js'
5
5
 
6
6
  import { DataSet } from './types.js'
@@ -95,7 +95,7 @@ export class OxDataEntryForm extends LitElement {
95
95
  const value = this.value && this.value[tag]
96
96
 
97
97
  const elements = samples.map((_, idx) => {
98
- const v = quota <= 1 ? value : value instanceof Array ? value[idx] : undefined
98
+ const v = value instanceof Array ? value[idx] : idx === 0 ? value : undefined
99
99
 
100
100
  switch (type) {
101
101
  case 'select':
@@ -141,19 +141,14 @@ export class OxDataEntryForm extends LitElement {
141
141
  const dataItems = this.dataSet!.dataItems
142
142
 
143
143
  return (dataItems || []).reduce((sum, dataItem) => {
144
- const { tag, quota, type } = dataItem
144
+ const { tag, type } = dataItem
145
145
 
146
146
  const editors = Array.prototype.slice.call(
147
147
  this.renderRoot.querySelectorAll(`[name=${tag}]`) as NodeListOf<HTMLInputElement>
148
148
  ) as HTMLInputElement[]
149
149
 
150
150
  if (editors.length > 0) {
151
- sum[tag] =
152
- editors.length > 1
153
- ? editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
154
- : type === 'boolean'
155
- ? editors[0].checked
156
- : editors[0].value
151
+ sum[tag] = editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
157
152
  }
158
153
 
159
154
  return sum
@@ -1,25 +1,13 @@
1
1
  import '@operato/property-editor/ox-properties-dynamic-view.js'
2
2
 
3
- import { DataItem, DataItemSpecSet, DataItemSpecSetProvider } from './types.js'
4
- import { LitElement, PropertyValues, css, html } from 'lit'
3
+ import { css, html, LitElement, PropertyValues } from 'lit'
5
4
  import { customElement, property, queryAll, state } from 'lit/decorators.js'
6
5
 
6
+ import { OxDataUseCase } from './ox-data-use-case.js'
7
+ import { DataItem, DataItemSpecSet } from './types.js'
8
+
7
9
  @customElement('ox-data-item-spec')
8
10
  export class OxDataItemSpec extends LitElement {
9
- static registry: { [name: string]: DataItemSpecSetProvider } = {}
10
-
11
- public static registerProvider(name: string, provider: DataItemSpecSetProvider) {
12
- OxDataItemSpec.registry[name] = provider
13
- }
14
-
15
- public static getProviderNames() {
16
- return Object.keys(OxDataItemSpec.registry)
17
- }
18
-
19
- public static getProvider(name: string): DataItemSpecSetProvider | undefined {
20
- return OxDataItemSpec.registry[name]
21
- }
22
-
23
11
  static styles = css`
24
12
  :host {
25
13
  display: flex;
@@ -61,7 +49,7 @@ export class OxDataItemSpec extends LitElement {
61
49
 
62
50
  updated(changes: PropertyValues<this>) {
63
51
  if (changes.has('dataItem')) {
64
- this.dataItemSpecSets = Object.values(OxDataItemSpec.registry).map(provider => provider(this.dataItem!))
52
+ this.dataItemSpecSets = OxDataUseCase.getUseCases().map(useCase => useCase.getSpecification(this.dataItem!))
65
53
  }
66
54
  }
67
55
 
@@ -0,0 +1,56 @@
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
+
18
+ h3 {
19
+ margin: var(--title-margin);
20
+ padding-top: 12px;
21
+ font: var(--title-font);
22
+ color: var(--title-text-color);
23
+ }
24
+
25
+ [page-description] {
26
+ margin: var(--page-description-margin);
27
+ font: var(--page-description-font);
28
+ color: var(--page-description-color);
29
+ }
30
+ `
31
+
32
+ @property({ type: Object }) dataSet?: DataSet
33
+ @property({ type: Object }) dataOoc?: DataOoc
34
+
35
+ render() {
36
+ const history = this.dataOoc?.history || []
37
+ const formatter = new Intl.DateTimeFormat(navigator.language, { dateStyle: 'full', timeStyle: 'short' })
38
+
39
+ return html`
40
+ <ox-data-sample-view .dataSample=${this.dataOoc} .dataSet=${this.dataSet}></ox-data-sample-view>
41
+
42
+ <h3>history</h3>
43
+ ${history.map(
44
+ ({ user, state, comment, timestamp }) => html`
45
+ <p page-description>
46
+ ${formatter.format(new Date(timestamp))}
47
+ <br />
48
+ ${state} by ${user.name}
49
+ <br />
50
+ ${comment}
51
+ </p>
52
+ `
53
+ )}
54
+ `
55
+ }
56
+ }
@@ -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 CHANGED
@@ -15,6 +15,7 @@ export type DataItem = {
15
15
  options: TypeOptions
16
16
  unit: string
17
17
  quota: number
18
+ spec: { [useCase: string]: any }
18
19
  }
19
20
 
20
21
  export type DataSet = {
@@ -24,19 +25,46 @@ export type DataSet = {
24
25
  useCase: string
25
26
  active: boolean
26
27
  dataItems: DataItem[]
28
+ spec: { [dataItem: string]: { [useCase: string]: any } }
27
29
  }
28
30
 
29
31
  export type DataItemSpec = {
30
32
  type: string
31
33
  label: string
32
34
  name: string
33
- property: { [option: string]: any }
35
+ property?: { [option: string]: any }
34
36
  }
35
37
 
36
38
  export type DataItemSpecSet = {
37
39
  name: string
38
40
  description: string
41
+ help: string
39
42
  specs: DataItemSpec[]
40
43
  }
41
44
 
42
- export type DataItemSpecSetProvider = (dataItem: DataItem) => DataItemSpecSet
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 }