@platforma-sdk/model 1.10.2 → 1.12.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/dist/components/PlDataTable.d.ts +9 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +344 -281
- package/dist/index.mjs.map +1 -1
- package/dist/render/api.d.ts +2 -1
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/util/label.d.ts +48 -0
- package/dist/render/util/label.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/PlDataTable.ts +12 -12
- package/src/render/api.ts +13 -5
- package/src/render/util/label.test.ts +98 -0
- package/src/render/util/label.ts +147 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../src/render/util/label.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;EAKrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAGpD,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;WAAsB,CAAC;AACzC,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC;AAQ1C,wBAAgB,YAAY,CAAC,CAAC,EAC5B,MAAM,EAAE,CAAC,EAAE,EACX,aAAa,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,WAAW,EACtC,GAAG,GAAE,kBAAuB,GAC3B,gBAAgB,CAAC,CAAC,CAAC,EAAE,CA4GvB"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PlatformaSDKVersion = "1.
|
|
1
|
+
export declare const PlatformaSDKVersion = "1.12.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -90,33 +90,33 @@ export type PlTableFilterNumberNotEquals = {
|
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
/** PlTableFilters numeric filter entry */
|
|
93
|
-
export type
|
|
93
|
+
export type PlTableFilterNumberGreaterThan = {
|
|
94
94
|
/** Predicate type */
|
|
95
|
-
type: '
|
|
95
|
+
type: 'number_greaterThan';
|
|
96
96
|
/** Referense value */
|
|
97
97
|
reference: number;
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
/** PlTableFilters numeric filter entry */
|
|
101
|
-
export type
|
|
101
|
+
export type PlTableFilterNumberGreaterThanOrEqualTo = {
|
|
102
102
|
/** Predicate type */
|
|
103
|
-
type: '
|
|
103
|
+
type: 'number_greaterThanOrEqualTo';
|
|
104
104
|
/** Referense value */
|
|
105
105
|
reference: number;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
/** PlTableFilters numeric filter entry */
|
|
109
|
-
export type
|
|
109
|
+
export type PlTableFilterNumberLessThan = {
|
|
110
110
|
/** Predicate type */
|
|
111
|
-
type: '
|
|
111
|
+
type: 'number_lessThan';
|
|
112
112
|
/** Referense value */
|
|
113
113
|
reference: number;
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
/** PlTableFilters numeric filter entry */
|
|
117
|
-
export type
|
|
117
|
+
export type PlTableFilterNumberLessThanOrEqualTo = {
|
|
118
118
|
/** Predicate type */
|
|
119
|
-
type: '
|
|
119
|
+
type: 'number_lessThanOrEqualTo';
|
|
120
120
|
/** Referense value */
|
|
121
121
|
reference: number;
|
|
122
122
|
};
|
|
@@ -140,10 +140,10 @@ export type PlTableFilterNumber =
|
|
|
140
140
|
| PlTableFilterCommon
|
|
141
141
|
| PlTableFilterNumberEquals
|
|
142
142
|
| PlTableFilterNumberNotEquals
|
|
143
|
-
|
|
|
144
|
-
|
|
|
145
|
-
|
|
|
146
|
-
|
|
|
143
|
+
| PlTableFilterNumberGreaterThan
|
|
144
|
+
| PlTableFilterNumberGreaterThanOrEqualTo
|
|
145
|
+
| PlTableFilterNumberLessThan
|
|
146
|
+
| PlTableFilterNumberLessThanOrEqualTo
|
|
147
147
|
| PlTableFilterNumberBetween;
|
|
148
148
|
/** All types of PlTableFilters numeric filter entries */
|
|
149
149
|
export type PlTableFilterNumberType = PlTableFilterNumber['type'];
|
package/src/render/api.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { getCfgRenderCtx } from '../internal';
|
|
|
24
24
|
import { TreeNodeAccessor } from './accessor';
|
|
25
25
|
import { FutureRef } from './future';
|
|
26
26
|
import { GlobalCfgRenderCtx, MainAccessorName, StagingAccessorName } from './internal';
|
|
27
|
+
import { deriveLabels, LabelDerivationOps } from './util/label';
|
|
27
28
|
|
|
28
29
|
export class ResultPool {
|
|
29
30
|
private readonly ctx: GlobalCfgRenderCtx = getCfgRenderCtx();
|
|
@@ -40,13 +41,20 @@ export class ResultPool {
|
|
|
40
41
|
|
|
41
42
|
public getOptions(
|
|
42
43
|
predicate: (spec: PObjectSpec) => boolean,
|
|
43
|
-
|
|
44
|
+
label?: ((spec: PObjectSpec, ref: Ref) => string) | LabelDerivationOps
|
|
44
45
|
): Option[] {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const filtered = this.getSpecs().entries.filter((s) => predicate(s.obj));
|
|
47
|
+
if (typeof label === 'object' || typeof label === 'undefined') {
|
|
48
|
+
return deriveLabels(filtered, (o) => o.obj, label ?? {}).map(
|
|
49
|
+
({ value: { ref }, label }) => ({
|
|
50
|
+
ref,
|
|
51
|
+
label
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
} else
|
|
55
|
+
return filtered.map((s) => ({
|
|
48
56
|
ref: s.ref,
|
|
49
|
-
label:
|
|
57
|
+
label: label(s.obj, s.ref)
|
|
50
58
|
}));
|
|
51
59
|
}
|
|
52
60
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { test, expect } from '@jest/globals';
|
|
2
|
+
import { deriveLabels, PAnnotationLabel, PAnnotationTrace, Trace } from './label';
|
|
3
|
+
import { PColumnSpec } from '@milaboratories/pl-model-common';
|
|
4
|
+
|
|
5
|
+
function tracesToSpecs(traces: Trace[]) {
|
|
6
|
+
return traces.map(
|
|
7
|
+
(t) =>
|
|
8
|
+
({
|
|
9
|
+
kind: 'PColumn',
|
|
10
|
+
name: 'name',
|
|
11
|
+
valueType: 'Int',
|
|
12
|
+
annotations: {
|
|
13
|
+
[PAnnotationTrace]: JSON.stringify(t),
|
|
14
|
+
[PAnnotationLabel]: 'Label'
|
|
15
|
+
},
|
|
16
|
+
axesSpec: []
|
|
17
|
+
}) satisfies PColumnSpec
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
test.each<{ name: string; traces: Trace[]; labels: string[] }>([
|
|
21
|
+
{
|
|
22
|
+
name: 'simple',
|
|
23
|
+
traces: [[{ type: 't1', label: 'L1' }], [{ type: 't1', label: 'L2' }]],
|
|
24
|
+
labels: ['L1', 'L2']
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'later wins',
|
|
28
|
+
traces: [
|
|
29
|
+
[
|
|
30
|
+
{ type: 't1', label: 'T1L1' },
|
|
31
|
+
{ type: 't2', label: 'T2L1' }
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
{ type: 't1', label: 'T1L2' },
|
|
35
|
+
{ type: 't2', label: 'T2L2' }
|
|
36
|
+
]
|
|
37
|
+
],
|
|
38
|
+
labels: ['T2L1', 'T2L2']
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'importance wins',
|
|
42
|
+
traces: [
|
|
43
|
+
[
|
|
44
|
+
{ type: 't1', importance: 100, label: 'T1L1' },
|
|
45
|
+
{ type: 't2', label: 'T2L1' }
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
{ type: 't1', importance: 100, label: 'T1L2' },
|
|
49
|
+
{ type: 't2', label: 'T2L2' }
|
|
50
|
+
]
|
|
51
|
+
],
|
|
52
|
+
labels: ['T1L1', 'T1L2']
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'uniqueness wins',
|
|
56
|
+
traces: [
|
|
57
|
+
[
|
|
58
|
+
{ type: 't1', label: 'T1L1' },
|
|
59
|
+
{ type: 't2', label: 'T2L1' }
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
{ type: 't1', label: 'T1L2' },
|
|
63
|
+
{ type: 't2', label: 'T2L1' }
|
|
64
|
+
]
|
|
65
|
+
],
|
|
66
|
+
labels: ['T1L1', 'T1L2']
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'combinatoric solution',
|
|
70
|
+
traces: [
|
|
71
|
+
[
|
|
72
|
+
{ type: 't1', label: 'T1L1' },
|
|
73
|
+
{ type: 't2', label: 'T2L1' }
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
{ type: 't1', label: 'T1L1' },
|
|
77
|
+
{ type: 't2', label: 'T2L2' }
|
|
78
|
+
],
|
|
79
|
+
[
|
|
80
|
+
{ type: 't1', label: 'T1L2' },
|
|
81
|
+
{ type: 't2', label: 'T2L2' }
|
|
82
|
+
]
|
|
83
|
+
],
|
|
84
|
+
labels: ['T1L1 / T2L1', 'T1L1 / T2L2', 'T1L2 / T2L2']
|
|
85
|
+
}
|
|
86
|
+
])('test label derivation: $name', ({ name, traces, labels }) => {
|
|
87
|
+
expect(deriveLabels(tracesToSpecs(traces), (s) => s).map((r) => r.label)).toEqual(labels);
|
|
88
|
+
expect(
|
|
89
|
+
deriveLabels(tracesToSpecs(traces), (s) => s, { includeNativeLabel: true }).map((r) => r.label)
|
|
90
|
+
).toEqual(labels.map((l) => 'Label / ' + l));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('test fallback to native labels in label derivation', () => {
|
|
94
|
+
expect(deriveLabels(tracesToSpecs([[], []]), (s) => s).map((r) => r.label)).toEqual([
|
|
95
|
+
'Label',
|
|
96
|
+
'Label'
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { PObjectSpec } from '@milaboratories/pl-model-common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export const PAnnotationLabel = 'pl7.app/label';
|
|
5
|
+
export const PAnnotationTrace = 'pl7.app/trace';
|
|
6
|
+
|
|
7
|
+
export type RecordsWithLabel<T> = {
|
|
8
|
+
value: T;
|
|
9
|
+
label: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type LabelDerivationOps = {
|
|
13
|
+
includeNativeLabel?: boolean;
|
|
14
|
+
separator?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const TraceEntry = z.object({
|
|
18
|
+
type: z.string(),
|
|
19
|
+
importance: z.number().optional(),
|
|
20
|
+
id: z.string().optional(),
|
|
21
|
+
label: z.string()
|
|
22
|
+
});
|
|
23
|
+
export type TraceEntry = z.infer<typeof TraceEntry>;
|
|
24
|
+
type FullTraceEntry = TraceEntry & { fullType: string; occurenceIndex: number };
|
|
25
|
+
|
|
26
|
+
export const Trace = z.array(TraceEntry);
|
|
27
|
+
export type Trace = z.infer<typeof Trace>;
|
|
28
|
+
type FullTrace = FullTraceEntry[];
|
|
29
|
+
|
|
30
|
+
const DistancePenalty = 0.001;
|
|
31
|
+
|
|
32
|
+
const LabelType = '__LABEL__';
|
|
33
|
+
const LabelTypeFull = '__LABEL__@1';
|
|
34
|
+
|
|
35
|
+
export function deriveLabels<T>(
|
|
36
|
+
values: T[],
|
|
37
|
+
specExtractor: (obj: T) => PObjectSpec,
|
|
38
|
+
ops: LabelDerivationOps = {}
|
|
39
|
+
): RecordsWithLabel<T>[] {
|
|
40
|
+
const importances = new Map<string, number>();
|
|
41
|
+
|
|
42
|
+
// number of times certain type occured among all of the
|
|
43
|
+
const numberOfRecordsWithType = new Map<string, number>();
|
|
44
|
+
|
|
45
|
+
const enrichedRecords = values.map((value) => {
|
|
46
|
+
const spec = specExtractor(value);
|
|
47
|
+
const label = spec.annotations?.[PAnnotationLabel];
|
|
48
|
+
const traceStr = spec.annotations?.[PAnnotationTrace];
|
|
49
|
+
const trace = (traceStr ? Trace.safeParse(JSON.parse(traceStr)).data : undefined) ?? [];
|
|
50
|
+
|
|
51
|
+
if (label) trace.splice(0, 0, { label, type: LabelType, importance: -2 });
|
|
52
|
+
|
|
53
|
+
const fullTrace: FullTrace = [];
|
|
54
|
+
|
|
55
|
+
const occurences = new Map<string, number>();
|
|
56
|
+
for (let i = trace.length - 1; i >= 0; --i) {
|
|
57
|
+
const { type: typeName } = trace[i];
|
|
58
|
+
const importance = trace[i].importance ?? 0;
|
|
59
|
+
const occurenceIndex = (occurences.get(typeName) ?? 0) + 1;
|
|
60
|
+
occurences.set(typeName, occurenceIndex);
|
|
61
|
+
const fullType = `${typeName}@${occurenceIndex}`;
|
|
62
|
+
numberOfRecordsWithType.set(fullType, (numberOfRecordsWithType.get(fullType) ?? 0) + 1);
|
|
63
|
+
importances.set(
|
|
64
|
+
fullType,
|
|
65
|
+
Math.max(
|
|
66
|
+
importances.get(fullType) ?? Number.NEGATIVE_INFINITY,
|
|
67
|
+
importance - (trace.length - i) * DistancePenalty
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
fullTrace.push({ ...trace[i], fullType, occurenceIndex });
|
|
71
|
+
}
|
|
72
|
+
fullTrace.reverse();
|
|
73
|
+
return {
|
|
74
|
+
value,
|
|
75
|
+
spec,
|
|
76
|
+
label,
|
|
77
|
+
fullTrace
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// excluding repeated types (i.e. ..@2, ..@3, etc.) not found in some records
|
|
82
|
+
const mainTypes: string[] = [];
|
|
83
|
+
// repeated types (i.e. ..@2, ..@3, etc.) not found in some records
|
|
84
|
+
const secondaryTypes: string[] = [];
|
|
85
|
+
|
|
86
|
+
const allTypeRecords = [...importances];
|
|
87
|
+
// sorting: most important types go first
|
|
88
|
+
allTypeRecords.sort(([, i1], [, i2]) => i2 - i1);
|
|
89
|
+
|
|
90
|
+
for (const [typeName] of allTypeRecords) {
|
|
91
|
+
if (typeName.endsWith('@1') || numberOfRecordsWithType.get(typeName) === values.length)
|
|
92
|
+
mainTypes.push(typeName);
|
|
93
|
+
else secondaryTypes.push(typeName);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const calculate = (includedTypes: Set<string>) =>
|
|
97
|
+
enrichedRecords.map((r) => {
|
|
98
|
+
const labelSet = r.fullTrace
|
|
99
|
+
.filter((fm) => includedTypes.has(fm.fullType))
|
|
100
|
+
.map((fm) => fm.label);
|
|
101
|
+
const sep = ops.separator ?? ' / ';
|
|
102
|
+
return {
|
|
103
|
+
label: labelSet.join(sep),
|
|
104
|
+
value: r.value
|
|
105
|
+
} satisfies RecordsWithLabel<T>;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (mainTypes.length === 0) {
|
|
109
|
+
if (secondaryTypes.length !== 0) throw new Error('Assertion error.');
|
|
110
|
+
return calculate(new Set(LabelTypeFull));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//
|
|
114
|
+
// includedTypes = 2
|
|
115
|
+
// * *
|
|
116
|
+
// T0 T1 T2 T3 T4 T5
|
|
117
|
+
// *
|
|
118
|
+
// additinalType = 3
|
|
119
|
+
//
|
|
120
|
+
// Resulting set: T0, T1, T3
|
|
121
|
+
//
|
|
122
|
+
let includedTypes = 0;
|
|
123
|
+
let additinalType = 0;
|
|
124
|
+
while (includedTypes < mainTypes.length) {
|
|
125
|
+
const currentSet = new Set<string>();
|
|
126
|
+
for (let i = 0; i < includedTypes; ++i) currentSet.add(mainTypes[i]);
|
|
127
|
+
currentSet.add(mainTypes[additinalType]);
|
|
128
|
+
|
|
129
|
+
const candidateResult = calculate(currentSet);
|
|
130
|
+
|
|
131
|
+
// checking if labels uniquely separate our records
|
|
132
|
+
if (new Set(candidateResult.map((c) => c.label)).size === values.length) {
|
|
133
|
+
if (ops.includeNativeLabel) {
|
|
134
|
+
currentSet.add(LabelTypeFull);
|
|
135
|
+
return calculate(currentSet);
|
|
136
|
+
} else return candidateResult;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
additinalType++;
|
|
140
|
+
if (additinalType == mainTypes.length) {
|
|
141
|
+
includedTypes++;
|
|
142
|
+
additinalType = includedTypes;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return calculate(new Set([...mainTypes, ...secondaryTypes]));
|
|
147
|
+
}
|