@platforma-sdk/model 1.10.12 → 1.13.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 +18 -2
- package/dist/components/PlDataTable.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +531 -331
- package/dist/index.mjs.map +1 -1
- package/dist/render/api.d.ts +15 -2
- 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/render/util/resource_map.d.ts +16 -2
- package/dist/render/util/resource_map.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/PlDataTable.ts +49 -6
- package/src/render/api.ts +63 -6
- package/src/render/util/label.test.ts +98 -0
- package/src/render/util/label.ts +147 -0
- package/src/render/util/resource_map.ts +141 -6
package/dist/render/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/render/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,OAAO,EACP,WAAW,EACX,SAAS,EACT,YAAY,EACZ,OAAO,EACP,WAAW,EACX,cAAc,EACd,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,GAAG,EACH,gBAAgB,EAChB,YAAY,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/render/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PObjectSpec } from '@milaboratories/pl-model-common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const PAnnotationLabel = "pl7.app/label";
|
|
4
|
+
export declare const PAnnotationTrace = "pl7.app/trace";
|
|
5
|
+
export type RecordsWithLabel<T> = {
|
|
6
|
+
value: T;
|
|
7
|
+
label: string;
|
|
8
|
+
};
|
|
9
|
+
export type LabelDerivationOps = {
|
|
10
|
+
includeNativeLabel?: boolean;
|
|
11
|
+
separator?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const TraceEntry: z.ZodObject<{
|
|
14
|
+
type: z.ZodString;
|
|
15
|
+
importance: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
id: z.ZodOptional<z.ZodString>;
|
|
17
|
+
label: z.ZodString;
|
|
18
|
+
}, "strip", z.ZodTypeAny, {
|
|
19
|
+
type: string;
|
|
20
|
+
label: string;
|
|
21
|
+
id?: string | undefined;
|
|
22
|
+
importance?: number | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
type: string;
|
|
25
|
+
label: string;
|
|
26
|
+
id?: string | undefined;
|
|
27
|
+
importance?: number | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
export type TraceEntry = z.infer<typeof TraceEntry>;
|
|
30
|
+
export declare const Trace: z.ZodArray<z.ZodObject<{
|
|
31
|
+
type: z.ZodString;
|
|
32
|
+
importance: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
id: z.ZodOptional<z.ZodString>;
|
|
34
|
+
label: z.ZodString;
|
|
35
|
+
}, "strip", z.ZodTypeAny, {
|
|
36
|
+
type: string;
|
|
37
|
+
label: string;
|
|
38
|
+
id?: string | undefined;
|
|
39
|
+
importance?: number | undefined;
|
|
40
|
+
}, {
|
|
41
|
+
type: string;
|
|
42
|
+
label: string;
|
|
43
|
+
id?: string | undefined;
|
|
44
|
+
importance?: number | undefined;
|
|
45
|
+
}>, "many">;
|
|
46
|
+
export type Trace = z.infer<typeof Trace>;
|
|
47
|
+
export declare function deriveLabels<T>(values: T[], specExtractor: (obj: T) => PObjectSpec, ops?: LabelDerivationOps): RecordsWithLabel<T>[];
|
|
48
|
+
//# sourceMappingURL=label.d.ts.map
|
|
@@ -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"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { TreeNodeAccessor } from '../accessor';
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
2
|
+
export declare const RT_RESOURCE_MAP: string;
|
|
3
|
+
export declare const RT_RESOURCE_MAP_PARTITIONED: string;
|
|
4
|
+
export declare const RT_JSON_PARTITIONED: string;
|
|
5
|
+
export declare const RT_BINARY_PARTITIONED: string;
|
|
6
|
+
export declare const RT_JSON_SUPER_PARTITIONED: string;
|
|
7
|
+
export declare const RT_BINARY_SUPER_PARTITIONED: string;
|
|
4
8
|
export type PColumnKey = (string | number)[];
|
|
5
9
|
export type PColumnResourceMapEntry<T> = {
|
|
6
10
|
key: PColumnKey;
|
|
@@ -12,4 +16,14 @@ export type PColumnResourceMapData<T> = {
|
|
|
12
16
|
};
|
|
13
17
|
export declare function parseResourceMap<T>(acc: TreeNodeAccessor | undefined, resourceParser: (acc: TreeNodeAccessor) => T | undefined, addEntriesWithNoData: false): PColumnResourceMapData<NonNullable<T>>;
|
|
14
18
|
export declare function parseResourceMap<T>(acc: TreeNodeAccessor | undefined, resourceParser: (acc: TreeNodeAccessor) => T | undefined, addEntriesWithNoData: true): PColumnResourceMapData<T | undefined>;
|
|
19
|
+
export type PColumnKeyList = {
|
|
20
|
+
/** array of keys */
|
|
21
|
+
data: PColumnKey[];
|
|
22
|
+
/** length of partition key */
|
|
23
|
+
keyLength: number;
|
|
24
|
+
};
|
|
25
|
+
/** Returns a list of all partition keys appeared in the p-column */
|
|
26
|
+
export declare function getPartitionKeysList(acc: TreeNodeAccessor | undefined): PColumnKeyList | undefined;
|
|
27
|
+
/** Returns an array of unique partition keys for each column: the i-th element in the resulting 2d array contains all unique values of i-th partition axis. */
|
|
28
|
+
export declare function getUniquePartitionKeys(acc: TreeNodeAccessor | undefined): (string | number)[][] | undefined;
|
|
15
29
|
//# sourceMappingURL=resource_map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource_map.d.ts","sourceRoot":"","sources":["../../../src/render/util/resource_map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"resource_map.d.ts","sourceRoot":"","sources":["../../../src/render/util/resource_map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,eAAe,QAA6B,CAAC;AAC1D,eAAO,MAAM,2BAA2B,QAAyC,CAAC;AAElF,eAAO,MAAM,mBAAmB,QAAiC,CAAC;AAClE,eAAO,MAAM,qBAAqB,QAAmC,CAAC;AAGtE,eAAO,MAAM,yBAAyB,QAAqC,CAAC;AAC5E,eAAO,MAAM,2BAA2B,QAAuC,CAAC;AAEhF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AAE7C,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI;IACvC,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI;IACtC,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;CACpC,CAAC;AA8CF,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,gBAAgB,GAAG,SAAS,EACjC,cAAc,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,CAAC,GAAG,SAAS,EACxD,oBAAoB,EAAE,KAAK,GAC1B,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1C,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,gBAAgB,GAAG,SAAS,EACjC,cAAc,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,CAAC,GAAG,SAAS,EACxD,oBAAoB,EAAE,IAAI,GACzB,sBAAsB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAYzC,MAAM,MAAM,cAAc,GAAG;IAC3B,oBAAoB;IACpB,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAaF,oEAAoE;AACpE,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,gBAAgB,GAAG,SAAS,GAChC,cAAc,GAAG,SAAS,CAoE5B;AAED,+JAA+J;AAE/J,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,gBAAgB,GAAG,SAAS,GAChC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,GAAG,SAAS,CAsBnC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PlatformaSDKVersion = "1.
|
|
1
|
+
export declare const PlatformaSDKVersion = "1.13.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,WAAW,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AxisId,
|
|
2
3
|
getAxisId,
|
|
3
4
|
isPColumn,
|
|
4
5
|
JoinEntry,
|
|
5
6
|
matchAxisId,
|
|
6
7
|
PColumn,
|
|
7
8
|
PColumnIdAndSpec,
|
|
9
|
+
PObjectId,
|
|
8
10
|
PTableHandle,
|
|
9
11
|
PTableRecordFilter,
|
|
10
12
|
PTableSorting
|
|
@@ -38,6 +40,22 @@ export type PlDataTableGridState = {
|
|
|
38
40
|
sheets?: Record<string, string | number>;
|
|
39
41
|
};
|
|
40
42
|
|
|
43
|
+
export type PlDataTableSheet = {
|
|
44
|
+
/** id of the axis to use */
|
|
45
|
+
axis: AxisId;
|
|
46
|
+
/** id of label column to use in filter instead of axis */
|
|
47
|
+
column?: PObjectId;
|
|
48
|
+
/** options to show in the filter tan */
|
|
49
|
+
options: {
|
|
50
|
+
/** value of the option (should be one of the values in the axis or column) */
|
|
51
|
+
value: string | number;
|
|
52
|
+
/** corresponding label */
|
|
53
|
+
label: string;
|
|
54
|
+
}[];
|
|
55
|
+
/** default (selected) value */
|
|
56
|
+
defaultValue?: string | number;
|
|
57
|
+
};
|
|
58
|
+
|
|
41
59
|
/**
|
|
42
60
|
* Params used to get p-table handle from the driver
|
|
43
61
|
*/
|
|
@@ -260,7 +278,8 @@ export type PlTableFiltersModel = {
|
|
|
260
278
|
export function createPlDataTable<A, U>(
|
|
261
279
|
ctx: RenderCtx<A, U>,
|
|
262
280
|
columns: PColumn<TreeNodeAccessor>[],
|
|
263
|
-
tableState?: PlDataTableState
|
|
281
|
+
tableState?: PlDataTableState,
|
|
282
|
+
filters?: PTableRecordFilter[]
|
|
264
283
|
): PTableHandle {
|
|
265
284
|
const allLabelCols = ctx.resultPool
|
|
266
285
|
.getData()
|
|
@@ -268,20 +287,44 @@ export function createPlDataTable<A, U>(
|
|
|
268
287
|
.filter(isPColumn)
|
|
269
288
|
.filter((p) => p.spec.name === 'pl7.app/label' && p.spec.axesSpec.length === 1);
|
|
270
289
|
|
|
271
|
-
const
|
|
290
|
+
const labelColumns = new Map<PObjectId, PColumn<TreeNodeAccessor>>;
|
|
272
291
|
for (const col of columns) {
|
|
273
292
|
for (const axis of col.spec.axesSpec) {
|
|
274
293
|
const axisId = getAxisId(axis);
|
|
275
294
|
for (const match of allLabelCols) {
|
|
276
295
|
if (matchAxisId(axisId, getAxisId(match.spec.axesSpec[0]))) {
|
|
277
|
-
|
|
296
|
+
labelColumns.set(match.id, match);
|
|
278
297
|
}
|
|
279
298
|
}
|
|
280
299
|
}
|
|
281
300
|
}
|
|
301
|
+
|
|
282
302
|
return ctx.createPTable({
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
303
|
+
src: {
|
|
304
|
+
type: 'outer',
|
|
305
|
+
primary: {
|
|
306
|
+
type: 'full',
|
|
307
|
+
entries: columns.map((c) => ({ type: 'column', column: c }))
|
|
308
|
+
},
|
|
309
|
+
secondary: Array.from(labelColumns.values()).map((c) => ({ type: 'column', column: c }))
|
|
310
|
+
},
|
|
311
|
+
filters: [...(tableState?.pTableParams?.filters ?? []), ...(filters ?? [])],
|
|
312
|
+
sorting: tableState?.pTableParams?.sorting ?? []
|
|
286
313
|
});
|
|
287
314
|
}
|
|
315
|
+
|
|
316
|
+
export function createPlDataTableSheet<A, U>(
|
|
317
|
+
ctx: RenderCtx<A, U>,
|
|
318
|
+
axis: AxisId,
|
|
319
|
+
values: (string | number)[]
|
|
320
|
+
): PlDataTableSheet {
|
|
321
|
+
const labels = ctx.findLabels(axis);
|
|
322
|
+
return {
|
|
323
|
+
axis: getAxisId(axis),
|
|
324
|
+
options: values.map((v) => ({
|
|
325
|
+
value: v,
|
|
326
|
+
label: labels?.get(v) ?? v.toString()
|
|
327
|
+
})),
|
|
328
|
+
defaultValue: values[0]
|
|
329
|
+
};
|
|
330
|
+
}
|
package/src/render/api.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AxisId,
|
|
2
3
|
Option,
|
|
3
4
|
PColumn,
|
|
4
5
|
PColumnSpec,
|
|
@@ -14,6 +15,8 @@ import {
|
|
|
14
15
|
Ref,
|
|
15
16
|
ResultCollection,
|
|
16
17
|
ValueOrError,
|
|
18
|
+
ensurePColumn,
|
|
19
|
+
isPColumn,
|
|
17
20
|
isPColumnSpec,
|
|
18
21
|
mapPObjectData,
|
|
19
22
|
mapPTableDef,
|
|
@@ -24,6 +27,7 @@ import { getCfgRenderCtx } from '../internal';
|
|
|
24
27
|
import { TreeNodeAccessor } from './accessor';
|
|
25
28
|
import { FutureRef } from './future';
|
|
26
29
|
import { GlobalCfgRenderCtx, MainAccessorName, StagingAccessorName } from './internal';
|
|
30
|
+
import { deriveLabels, LabelDerivationOps } from './util/label';
|
|
27
31
|
|
|
28
32
|
export class ResultPool {
|
|
29
33
|
private readonly ctx: GlobalCfgRenderCtx = getCfgRenderCtx();
|
|
@@ -40,13 +44,20 @@ export class ResultPool {
|
|
|
40
44
|
|
|
41
45
|
public getOptions(
|
|
42
46
|
predicate: (spec: PObjectSpec) => boolean,
|
|
43
|
-
|
|
47
|
+
label?: ((spec: PObjectSpec, ref: Ref) => string) | LabelDerivationOps
|
|
44
48
|
): Option[] {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const filtered = this.getSpecs().entries.filter((s) => predicate(s.obj));
|
|
50
|
+
if (typeof label === 'object' || typeof label === 'undefined') {
|
|
51
|
+
return deriveLabels(filtered, (o) => o.obj, label ?? {}).map(
|
|
52
|
+
({ value: { ref }, label }) => ({
|
|
53
|
+
ref,
|
|
54
|
+
label
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
} else
|
|
58
|
+
return filtered.map((s) => ({
|
|
48
59
|
ref: s.ref,
|
|
49
|
-
label:
|
|
60
|
+
label: label(s.obj, s.ref)
|
|
50
61
|
}));
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -123,6 +134,17 @@ export class ResultPool {
|
|
|
123
134
|
);
|
|
124
135
|
}
|
|
125
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Returns data associated with the ref ensuring that it is a p-column.
|
|
139
|
+
* @param ref a Ref
|
|
140
|
+
* @returns p-column associated with the ref
|
|
141
|
+
*/
|
|
142
|
+
public getPColumnByRef(ref: Ref): PColumn<TreeNodeAccessor> | undefined {
|
|
143
|
+
const data = this.getDataByRef(ref);
|
|
144
|
+
if (!data) return undefined;
|
|
145
|
+
return ensurePColumn(data);
|
|
146
|
+
}
|
|
147
|
+
|
|
126
148
|
/**
|
|
127
149
|
* @param ref a Ref
|
|
128
150
|
* @returns object spec associated with the ref
|
|
@@ -139,6 +161,7 @@ export class ResultPool {
|
|
|
139
161
|
/**
|
|
140
162
|
* @param spec object specification
|
|
141
163
|
* @returns array of data objects with compatible specs
|
|
164
|
+
* @deprecated delete this method after Jan 1, 2025
|
|
142
165
|
*/
|
|
143
166
|
public findDataWithCompatibleSpec(spec: PColumnSpec): PObject<TreeNodeAccessor>[] {
|
|
144
167
|
const result: PObject<TreeNodeAccessor>[] = [];
|
|
@@ -243,6 +266,40 @@ export class RenderCtx<Args, UiState> {
|
|
|
243
266
|
|
|
244
267
|
public readonly resultPool = new ResultPool();
|
|
245
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Find labels data for a given axis id. It will search for a label column and return its data as a map.
|
|
271
|
+
* @returns a map of axis value => label
|
|
272
|
+
*/
|
|
273
|
+
public findLabels(axis: AxisId): Map<string | number, string> | undefined {
|
|
274
|
+
const dataPool = this.resultPool.getData();
|
|
275
|
+
for (const column of dataPool.entries) {
|
|
276
|
+
if (!isPColumn(column.obj)) continue;
|
|
277
|
+
|
|
278
|
+
const spec = column.obj.spec;
|
|
279
|
+
if (
|
|
280
|
+
spec.name === 'pl7.app/label' &&
|
|
281
|
+
spec.axesSpec.length === 1 &&
|
|
282
|
+
spec.axesSpec[0].name === axis.name &&
|
|
283
|
+
spec.axesSpec[0].type === axis.type &&
|
|
284
|
+
matchDomain(axis.domain, spec.axesSpec[0].domain)
|
|
285
|
+
) {
|
|
286
|
+
if (column.obj.data.resourceType.name !== 'PColumnData/Json') {
|
|
287
|
+
throw Error(`Expected JSON column for labels, got: ${column.obj.data.resourceType.name}`);
|
|
288
|
+
}
|
|
289
|
+
const labels = new Map<string | number, string>(
|
|
290
|
+
Object.entries(
|
|
291
|
+
column.obj.data.getDataAsJson<{
|
|
292
|
+
data: Record<string | number, string>;
|
|
293
|
+
}>().data
|
|
294
|
+
).map((e) => [JSON.parse(e[0])[0], e[1]])
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
return labels;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
|
|
246
303
|
public createPFrame(def: PFrameDef<TreeNodeAccessor>): PFrameHandle {
|
|
247
304
|
return this.ctx.createPFrame(def.map((c) => mapPObjectData(c, (d) => d.handle)));
|
|
248
305
|
}
|
|
@@ -268,7 +325,7 @@ export class RenderCtx<Args, UiState> {
|
|
|
268
325
|
if ('columns' in def) {
|
|
269
326
|
rawDef = {
|
|
270
327
|
src: {
|
|
271
|
-
type: '
|
|
328
|
+
type: 'full',
|
|
272
329
|
entries: def.columns.map((c) => ({ type: 'column', column: c }))
|
|
273
330
|
},
|
|
274
331
|
filters: def.filters ?? [],
|
|
@@ -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
|
+
}
|