@platforma-sdk/model 1.68.8 → 1.70.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/columns/column_collection_builder.cjs +1 -4
- package/dist/columns/column_collection_builder.cjs.map +1 -1
- package/dist/columns/column_collection_builder.d.ts +0 -2
- package/dist/columns/column_collection_builder.d.ts.map +1 -1
- package/dist/columns/column_collection_builder.js +1 -4
- package/dist/columns/column_collection_builder.js.map +1 -1
- package/dist/columns/ctx_column_sources.cjs +5 -8
- package/dist/columns/ctx_column_sources.cjs.map +1 -1
- package/dist/columns/ctx_column_sources.d.ts +4 -7
- package/dist/columns/ctx_column_sources.d.ts.map +1 -1
- package/dist/columns/ctx_column_sources.js +5 -8
- package/dist/columns/ctx_column_sources.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +23 -44
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +23 -44
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +2 -7
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +2 -7
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs +3 -18
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.js +4 -19
- package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
- package/dist/components/PlDataTable/labels.cjs +0 -25
- package/dist/components/PlDataTable/labels.cjs.map +1 -1
- package/dist/components/PlDataTable/labels.js +2 -26
- package/dist/components/PlDataTable/labels.js.map +1 -1
- package/dist/components/PlDatasetSelector/build_dataset_options.cjs +23 -11
- package/dist/components/PlDatasetSelector/build_dataset_options.cjs.map +1 -1
- package/dist/components/PlDatasetSelector/build_dataset_options.d.ts +9 -2
- package/dist/components/PlDatasetSelector/build_dataset_options.d.ts.map +1 -1
- package/dist/components/PlDatasetSelector/build_dataset_options.js +22 -11
- package/dist/components/PlDatasetSelector/build_dataset_options.js.map +1 -1
- package/dist/components/PlDatasetSelector/dataset_selection.cjs +20 -0
- package/dist/components/PlDatasetSelector/dataset_selection.cjs.map +1 -0
- package/dist/components/PlDatasetSelector/dataset_selection.d.ts +23 -0
- package/dist/components/PlDatasetSelector/dataset_selection.d.ts.map +1 -0
- package/dist/components/PlDatasetSelector/dataset_selection.js +19 -0
- package/dist/components/PlDatasetSelector/dataset_selection.js.map +1 -0
- package/dist/components/PlDatasetSelector/enrichment_discovery.cjs +75 -0
- package/dist/components/PlDatasetSelector/enrichment_discovery.cjs.map +1 -0
- package/dist/components/PlDatasetSelector/enrichment_discovery.js +73 -0
- package/dist/components/PlDatasetSelector/enrichment_discovery.js.map +1 -0
- package/dist/components/PlDatasetSelector/index.cjs +1 -0
- package/dist/components/PlDatasetSelector/index.d.ts +1 -0
- package/dist/components/PlDatasetSelector/index.js +1 -0
- package/dist/components/index.cjs +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/labels/derive_distinct_tooltips.cjs +0 -3
- package/dist/labels/derive_distinct_tooltips.cjs.map +1 -1
- package/dist/labels/derive_distinct_tooltips.js +0 -3
- package/dist/labels/derive_distinct_tooltips.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/package.json +8 -8
- package/src/columns/column_collection_builder.ts +0 -3
- package/src/columns/ctx_column_sources.ts +6 -12
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +3 -43
- package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +0 -10
- package/src/components/PlDataTable/createPlDataTable/utils.test.ts +5 -131
- package/src/components/PlDataTable/createPlDataTable/utils.ts +2 -26
- package/src/components/PlDatasetSelector/build_dataset_options.ts +48 -17
- package/src/components/PlDatasetSelector/dataset_selection.ts +37 -0
- package/src/components/PlDatasetSelector/enrichment_discovery.ts +111 -0
- package/src/components/PlDatasetSelector/index.ts +1 -0
- package/src/labels/derive_distinct_tooltips.test.ts +6 -16
- package/src/labels/derive_distinct_tooltips.ts +0 -3
|
@@ -21,7 +21,6 @@ import type { PlDataTableFilters, PlDataTableFilterSpecLeaf, PlDataTableModel }
|
|
|
21
21
|
import { upgradePlDataTableStateV2 } from "../state-migration";
|
|
22
22
|
import type { PlDataTableStateV2 } from "../state-migration";
|
|
23
23
|
import type { ColumnSelector, ColumnSnapshot, ColumnVariant, MatchingMode } from "../../../columns";
|
|
24
|
-
import { getAllLabelColumns, getMatchingLabelColumns } from "../labels";
|
|
25
24
|
import type { DeriveLabelsOptions } from "../../../labels/derive_distinct_labels";
|
|
26
25
|
import {
|
|
27
26
|
deriveAllLabels,
|
|
@@ -97,11 +96,6 @@ export function createPlDataTableV3<A, U>(
|
|
|
97
96
|
|
|
98
97
|
const splited = splitDiscoveredColumns(discovered);
|
|
99
98
|
|
|
100
|
-
const labelColumns = getMatchingLabelColumns(
|
|
101
|
-
[...splited.direct, ...splited.linked].map((v) => v.column),
|
|
102
|
-
getAllLabelColumns(ctx),
|
|
103
|
-
);
|
|
104
|
-
|
|
105
99
|
const derivedLabels = deriveAllLabels({
|
|
106
100
|
columns: discovered.map((dc) => ({
|
|
107
101
|
id: dc.column.id,
|
|
@@ -109,7 +103,6 @@ export function createPlDataTableV3<A, U>(
|
|
|
109
103
|
linkerPath: dc.path,
|
|
110
104
|
qualifications: dc.qualifications,
|
|
111
105
|
})),
|
|
112
|
-
labelColumns,
|
|
113
106
|
deriveLabelsOptions: {
|
|
114
107
|
includeNativeLabel: true,
|
|
115
108
|
...options.labelsOptions,
|
|
@@ -129,7 +122,6 @@ export function createPlDataTableV3<A, U>(
|
|
|
129
122
|
const annotated = annotateColumnGroups({
|
|
130
123
|
pframeSpec,
|
|
131
124
|
...splited,
|
|
132
|
-
labelColumns,
|
|
133
125
|
derivedLabels,
|
|
134
126
|
derivedTooltips,
|
|
135
127
|
displayOptions: options.displayOptions,
|
|
@@ -143,7 +135,6 @@ export function createPlDataTableV3<A, U>(
|
|
|
143
135
|
const columnIsAvailable = createColumnValidationById([
|
|
144
136
|
...annotated.direct.map((v) => v.column),
|
|
145
137
|
...annotated.linked.flatMap((lc) => [...lc.path.map((s) => s.linker), lc.column]),
|
|
146
|
-
...annotated.labels,
|
|
147
138
|
]);
|
|
148
139
|
|
|
149
140
|
const remapedDefaultFilters = remapFilterColumnIds(options.filters, discovered);
|
|
@@ -165,7 +156,6 @@ export function createPlDataTableV3<A, U>(
|
|
|
165
156
|
const secondaryGroups: SecondaryGroup<undefined | PColumnDataUniversal>[] = buildSecondaryGroups(
|
|
166
157
|
secondarySnapshots,
|
|
167
158
|
annotated.linked,
|
|
168
|
-
annotated.labels,
|
|
169
159
|
);
|
|
170
160
|
const fullDef = createPTableDefV3({
|
|
171
161
|
primaryJoinType,
|
|
@@ -181,7 +171,6 @@ export function createPlDataTableV3<A, U>(
|
|
|
181
171
|
const pframeHandle = ctx.createPFrame([
|
|
182
172
|
...annotated.direct.map((v) => resolveSnapshot(v.column)),
|
|
183
173
|
...annotated.linked.map((v) => resolveSnapshot(v.column)),
|
|
184
|
-
...annotated.labels,
|
|
185
174
|
...collectLinkerSnapshots(annotated.linked).map(resolveSnapshot),
|
|
186
175
|
]);
|
|
187
176
|
|
|
@@ -193,14 +182,13 @@ export function createPlDataTableV3<A, U>(
|
|
|
193
182
|
hiddenSpecs,
|
|
194
183
|
);
|
|
195
184
|
|
|
196
|
-
const visible = buildVisibleColumns(annotated, hiddenColumnIds
|
|
185
|
+
const visible = buildVisibleColumns(annotated, hiddenColumnIds);
|
|
197
186
|
const visibleDef = createPTableDefV3({
|
|
198
187
|
primaryJoinType,
|
|
199
188
|
primary: primaryEntries,
|
|
200
189
|
secondary: buildSecondaryGroups(
|
|
201
190
|
visible.direct.filter((c) => !c.isPrimary),
|
|
202
191
|
visible.linked,
|
|
203
|
-
visible.labels,
|
|
204
192
|
),
|
|
205
193
|
filters,
|
|
206
194
|
sorting,
|
|
@@ -229,13 +217,11 @@ type SplitDiscoveredColumns = {
|
|
|
229
217
|
type AnnotatedColumnGroups = {
|
|
230
218
|
readonly direct: TableColumnVariant[];
|
|
231
219
|
readonly linked: TableColumnVariant[];
|
|
232
|
-
readonly labels: PColumn<PColumnDataUniversal>[];
|
|
233
220
|
};
|
|
234
221
|
|
|
235
222
|
type VisibleColumns = {
|
|
236
223
|
readonly direct: TableColumnVariant[];
|
|
237
224
|
readonly linked: TableColumnVariant[];
|
|
238
|
-
readonly labels: PColumn<PColumnDataUniversal>[];
|
|
239
225
|
};
|
|
240
226
|
|
|
241
227
|
/** Split discovered columns into direct (no linker path) and linked (with linker path). */
|
|
@@ -262,26 +248,16 @@ function collectLinkerSnapshots(linked: TableColumnVariant[]): ColumnSnapshot<PO
|
|
|
262
248
|
function annotateColumnGroups(params: {
|
|
263
249
|
direct: TableColumnVariant[];
|
|
264
250
|
linked: TableColumnVariant[];
|
|
265
|
-
labelColumns: PColumn<PColumnDataUniversal>[];
|
|
266
251
|
derivedLabels: Record<string, string>;
|
|
267
252
|
derivedTooltips: Record<string, string>;
|
|
268
253
|
displayOptions?: ColumnsDisplayOptions;
|
|
269
254
|
pframeSpec: PFrameSpecDriver;
|
|
270
255
|
}): AnnotatedColumnGroups {
|
|
271
|
-
const {
|
|
272
|
-
direct,
|
|
273
|
-
linked,
|
|
274
|
-
labelColumns,
|
|
275
|
-
derivedLabels,
|
|
276
|
-
derivedTooltips,
|
|
277
|
-
displayOptions,
|
|
278
|
-
pframeSpec,
|
|
279
|
-
} = params;
|
|
256
|
+
const { direct, linked, derivedLabels, derivedTooltips, displayOptions, pframeSpec } = params;
|
|
280
257
|
|
|
281
258
|
const allColumnsForRules = [
|
|
282
259
|
...direct.map((v) => v.column),
|
|
283
260
|
...linked.map((v) => v.column),
|
|
284
|
-
...labelColumns,
|
|
285
261
|
...collectLinkerSnapshots(linked),
|
|
286
262
|
];
|
|
287
263
|
const visibilityByColId = evaluateRules(
|
|
@@ -314,15 +290,9 @@ function annotateColumnGroups(params: {
|
|
|
314
290
|
),
|
|
315
291
|
).map((lc) => ({ ...lc, path: annotateLinkerPath(derivedLabels, lc.path) }));
|
|
316
292
|
|
|
317
|
-
const labelColumnsAnnotated = flow(
|
|
318
|
-
(cols: PColumn<PColumnDataUniversal>[]) => withHidenAxesAnnotations(cols),
|
|
319
|
-
(cols) => withLabelAnnotations(derivedLabels, cols),
|
|
320
|
-
)(labelColumns);
|
|
321
|
-
|
|
322
293
|
return {
|
|
323
294
|
direct: directAnnotated,
|
|
324
295
|
linked: linkedAnnotated,
|
|
325
|
-
labels: labelColumnsAnnotated,
|
|
326
296
|
};
|
|
327
297
|
}
|
|
328
298
|
|
|
@@ -422,7 +392,6 @@ function validateSorting(sorting: PTableSorting[], isValidColumnId: (id: string)
|
|
|
422
392
|
function buildSecondaryGroups(
|
|
423
393
|
direct: TableColumnVariant[],
|
|
424
394
|
linked: TableColumnVariant[],
|
|
425
|
-
labels: PColumn<PColumnDataUniversal>[],
|
|
426
395
|
): SecondaryGroup<undefined | PColumnDataUniversal>[] {
|
|
427
396
|
return [
|
|
428
397
|
...direct.map(
|
|
@@ -436,16 +405,12 @@ function buildSecondaryGroups(
|
|
|
436
405
|
entries: [
|
|
437
406
|
...lc.path.map((s) => ({
|
|
438
407
|
column: resolveSnapshot(s.linker),
|
|
439
|
-
qualifications: s.qualifications,
|
|
440
408
|
})),
|
|
441
409
|
{ column: resolveSnapshot(lc.column), qualifications: lc.qualifications.forHit },
|
|
442
410
|
],
|
|
443
411
|
primaryQualifications: lc.qualifications.forQueries,
|
|
444
412
|
}),
|
|
445
413
|
),
|
|
446
|
-
...labels.map(
|
|
447
|
-
(c): SecondaryGroup<undefined | PColumnDataUniversal> => ({ entries: [{ column: c }] }),
|
|
448
|
-
),
|
|
449
414
|
];
|
|
450
415
|
}
|
|
451
416
|
|
|
@@ -490,15 +455,10 @@ function collectPreservedColumnIds(
|
|
|
490
455
|
function buildVisibleColumns(
|
|
491
456
|
annotated: AnnotatedColumnGroups,
|
|
492
457
|
hiddenColumns: Set<PObjectId>,
|
|
493
|
-
originalLabelColumns: PColumn<PColumnDataUniversal>[],
|
|
494
458
|
): VisibleColumns {
|
|
495
459
|
const direct = annotated.direct.filter((c) => !hiddenColumns.has(c.column.id));
|
|
496
460
|
const linked = annotated.linked.filter((c) => !hiddenColumns.has(c.column.id));
|
|
497
|
-
|
|
498
|
-
[...direct, ...linked].map((v) => v.column),
|
|
499
|
-
originalLabelColumns,
|
|
500
|
-
);
|
|
501
|
-
return { direct, linked, labels };
|
|
461
|
+
return { direct, linked };
|
|
502
462
|
}
|
|
503
463
|
|
|
504
464
|
/** Resolve a ColumnSnapshot to a PColumn with lazily-evaluated data. */
|
|
@@ -13,7 +13,6 @@ import { toColumnSnapshotProvider } from "../../../columns/column_snapshot_provi
|
|
|
13
13
|
import { collectCtxColumnSnapshotProviders } from "../../../columns/ctx_column_sources";
|
|
14
14
|
import { throwError } from "@milaboratories/helpers";
|
|
15
15
|
import type { ColumnsSelectorConfig, TableColumnVariant } from "./createPlDataTableV3";
|
|
16
|
-
import { isNil } from "es-toolkit";
|
|
17
16
|
|
|
18
17
|
export type DiscoverTableColumnOptions = {
|
|
19
18
|
sources?: ColumnSource[];
|
|
@@ -45,14 +44,6 @@ export function discoverTableColumnSnaphots(
|
|
|
45
44
|
try {
|
|
46
45
|
const variants = collection.findColumnVariants({
|
|
47
46
|
...(resolvedOptions.selector ?? {}),
|
|
48
|
-
exclude: [
|
|
49
|
-
...(Array.isArray(resolvedOptions.selector?.exclude)
|
|
50
|
-
? resolvedOptions.selector.exclude
|
|
51
|
-
: !isNil(resolvedOptions.selector.exclude)
|
|
52
|
-
? [resolvedOptions.selector.exclude]
|
|
53
|
-
: []),
|
|
54
|
-
{ name: "pl7.app/label" },
|
|
55
|
-
],
|
|
56
47
|
});
|
|
57
48
|
const anchors = collection.getAnchors();
|
|
58
49
|
return mapToTableColumnVariants(variants, anchors);
|
|
@@ -111,7 +102,6 @@ function mapToTableColumnVariants(
|
|
|
111
102
|
path: variant.path.map((p) => ({
|
|
112
103
|
type: "linker",
|
|
113
104
|
column: p.linker.id,
|
|
114
|
-
qualifications: p.qualifications,
|
|
115
105
|
})),
|
|
116
106
|
columnQualifications: variant.qualifications.forHit,
|
|
117
107
|
queriesQualifications: variant.qualifications.forQueries,
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Annotation,
|
|
3
|
-
canonicalizeJson,
|
|
4
|
-
getAxisId,
|
|
5
|
-
type AxisId,
|
|
6
|
-
type PColumnSpec,
|
|
7
|
-
type PObjectId,
|
|
8
|
-
} from "@milaboratories/pl-model-common";
|
|
1
|
+
import { Annotation, type PColumnSpec, type PObjectId } from "@milaboratories/pl-model-common";
|
|
9
2
|
import { SpecDriver } from "@milaboratories/pf-spec-driver";
|
|
10
3
|
import { describe, expect, test } from "vitest";
|
|
11
4
|
import { deriveAllLabels, evaluateRules, type LabelableColumn, type RuleColumn } from "./utils";
|
|
@@ -38,24 +31,6 @@ function makeLabelableColumn(
|
|
|
38
31
|
};
|
|
39
32
|
}
|
|
40
33
|
|
|
41
|
-
function makeLabelColumn(
|
|
42
|
-
axisSpec: PColumnSpec["axesSpec"][0],
|
|
43
|
-
label: string,
|
|
44
|
-
): { readonly spec: PColumnSpec } {
|
|
45
|
-
return {
|
|
46
|
-
spec: makeSpec({
|
|
47
|
-
name: "label-col",
|
|
48
|
-
valueType: "String",
|
|
49
|
-
annotations: { [Annotation.Label]: label },
|
|
50
|
-
axesSpec: [axisSpec],
|
|
51
|
-
}),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function axisKey(axis: PColumnSpec["axesSpec"][0]): string {
|
|
56
|
-
return canonicalizeJson<AxisId>(getAxisId(axis));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
34
|
// ---------------------------------------------------------------------------
|
|
60
35
|
// deriveAllLabels
|
|
61
36
|
// ---------------------------------------------------------------------------
|
|
@@ -75,7 +50,6 @@ describe("deriveAllLabels", () => {
|
|
|
75
50
|
|
|
76
51
|
const result = deriveAllLabels({
|
|
77
52
|
columns,
|
|
78
|
-
labelColumns: [],
|
|
79
53
|
deriveLabelsOptions: { includeNativeLabel: true },
|
|
80
54
|
});
|
|
81
55
|
|
|
@@ -83,7 +57,7 @@ describe("deriveAllLabels", () => {
|
|
|
83
57
|
expect(result["c2"]).toBe("Beta");
|
|
84
58
|
});
|
|
85
59
|
|
|
86
|
-
test("
|
|
60
|
+
test("does not produce axis labels", () => {
|
|
87
61
|
const axis = { type: "String", name: "sample" } as const;
|
|
88
62
|
const columns: LabelableColumn[] = [
|
|
89
63
|
makeLabelableColumn("c1", {
|
|
@@ -92,56 +66,17 @@ describe("deriveAllLabels", () => {
|
|
|
92
66
|
annotations: { [Annotation.Label]: "Score" },
|
|
93
67
|
}),
|
|
94
68
|
];
|
|
95
|
-
const labelColumns = [makeLabelColumn(axis, "Sample Name")];
|
|
96
|
-
|
|
97
|
-
const result = deriveAllLabels({
|
|
98
|
-
columns,
|
|
99
|
-
labelColumns,
|
|
100
|
-
deriveLabelsOptions: { includeNativeLabel: true },
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
expect(result[axisKey(axis)]).toBe("Sample Name");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test("falls back to axis annotation when no label column matches", () => {
|
|
107
|
-
const axis = { type: "String", name: "gene" } as const;
|
|
108
|
-
const columns: LabelableColumn[] = [
|
|
109
|
-
makeLabelableColumn("c1", {
|
|
110
|
-
name: "expr",
|
|
111
|
-
axesSpec: [{ ...axis, annotations: { [Annotation.Label]: "Gene ID" } }],
|
|
112
|
-
annotations: { [Annotation.Label]: "Expression" },
|
|
113
|
-
}),
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
const result = deriveAllLabels({
|
|
117
|
-
columns,
|
|
118
|
-
labelColumns: [],
|
|
119
|
-
deriveLabelsOptions: { includeNativeLabel: true },
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
expect(result[axisKey(axis)]).toBe("Gene ID");
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("axis with no label annotation and no label column gets 'Unlabeled'", () => {
|
|
126
|
-
const axis = { type: "Int", name: "idx" } as const;
|
|
127
|
-
const columns: LabelableColumn[] = [
|
|
128
|
-
makeLabelableColumn("c1", {
|
|
129
|
-
name: "val",
|
|
130
|
-
axesSpec: [axis],
|
|
131
|
-
annotations: { [Annotation.Label]: "Value" },
|
|
132
|
-
}),
|
|
133
|
-
];
|
|
134
69
|
|
|
135
70
|
const result = deriveAllLabels({
|
|
136
71
|
columns,
|
|
137
|
-
labelColumns: [],
|
|
138
72
|
deriveLabelsOptions: { includeNativeLabel: true },
|
|
139
73
|
});
|
|
140
74
|
|
|
141
|
-
expect(result
|
|
75
|
+
expect(Object.keys(result)).toEqual(["c1"]);
|
|
76
|
+
expect(result["c1"]).toBe("Score");
|
|
142
77
|
});
|
|
143
78
|
|
|
144
|
-
test("
|
|
79
|
+
test("distinct labels for columns sharing an axis", () => {
|
|
145
80
|
const sharedAxis = { type: "String", name: "sample" } as const;
|
|
146
81
|
const columns: LabelableColumn[] = [
|
|
147
82
|
makeLabelableColumn("c1", {
|
|
@@ -155,75 +90,15 @@ describe("deriveAllLabels", () => {
|
|
|
155
90
|
annotations: { [Annotation.Label]: "Score 2" },
|
|
156
91
|
}),
|
|
157
92
|
];
|
|
158
|
-
const labelColumns = [makeLabelColumn(sharedAxis, "Sample")];
|
|
159
93
|
|
|
160
94
|
const result = deriveAllLabels({
|
|
161
95
|
columns,
|
|
162
|
-
labelColumns,
|
|
163
96
|
deriveLabelsOptions: { includeNativeLabel: true },
|
|
164
97
|
});
|
|
165
98
|
|
|
166
|
-
// axis label appears once
|
|
167
|
-
expect(result[axisKey(sharedAxis)]).toBe("Sample");
|
|
168
|
-
// column labels are distinct
|
|
169
99
|
expect(result["c1"]).toBe("Score 1");
|
|
170
100
|
expect(result["c2"]).toBe("Score 2");
|
|
171
101
|
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
// deriveAxisLabels (tested through deriveAllLabels)
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
|
|
178
|
-
describe("deriveAxisLabels via deriveAllLabels", () => {
|
|
179
|
-
test("multiple axes each get their own label", () => {
|
|
180
|
-
const axis1 = { type: "String", name: "sample" } as const;
|
|
181
|
-
const axis2 = { type: "String", name: "chain" } as const;
|
|
182
|
-
const columns: LabelableColumn[] = [
|
|
183
|
-
makeLabelableColumn("c1", {
|
|
184
|
-
name: "value",
|
|
185
|
-
axesSpec: [axis1, axis2],
|
|
186
|
-
annotations: { [Annotation.Label]: "Value" },
|
|
187
|
-
}),
|
|
188
|
-
];
|
|
189
|
-
const labelColumns = [
|
|
190
|
-
makeLabelColumn(axis1, "Sample ID"),
|
|
191
|
-
makeLabelColumn(axis2, "Chain Type"),
|
|
192
|
-
];
|
|
193
|
-
|
|
194
|
-
const result = deriveAllLabels({
|
|
195
|
-
columns,
|
|
196
|
-
labelColumns,
|
|
197
|
-
deriveLabelsOptions: { includeNativeLabel: true },
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(result[axisKey(axis1)]).toBe("Sample ID");
|
|
201
|
-
expect(result[axisKey(axis2)]).toBe("Chain Type");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test("label column overrides axis annotation", () => {
|
|
205
|
-
const axis = {
|
|
206
|
-
type: "String",
|
|
207
|
-
name: "sample",
|
|
208
|
-
annotations: { [Annotation.Label]: "Axis Annotation Label" },
|
|
209
|
-
} as const;
|
|
210
|
-
const columns: LabelableColumn[] = [
|
|
211
|
-
makeLabelableColumn("c1", {
|
|
212
|
-
name: "v",
|
|
213
|
-
axesSpec: [axis],
|
|
214
|
-
annotations: { [Annotation.Label]: "V" },
|
|
215
|
-
}),
|
|
216
|
-
];
|
|
217
|
-
const labelColumns = [makeLabelColumn({ type: "String", name: "sample" }, "Label Col Label")];
|
|
218
|
-
|
|
219
|
-
const result = deriveAllLabels({
|
|
220
|
-
columns,
|
|
221
|
-
labelColumns,
|
|
222
|
-
deriveLabelsOptions: { includeNativeLabel: true },
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
expect(result[axisKey(axis)]).toBe("Label Col Label");
|
|
226
|
-
});
|
|
227
102
|
|
|
228
103
|
// Regression: LabelableColumn.linkerPath used to be `linkerPath` while
|
|
229
104
|
// deriveDistinctLabels.Entry expected `linkersPath`. Structural typing +
|
|
@@ -249,7 +124,6 @@ describe("deriveAxisLabels via deriveAllLabels", () => {
|
|
|
249
124
|
|
|
250
125
|
const result = deriveAllLabels({
|
|
251
126
|
columns,
|
|
252
|
-
labelColumns: [],
|
|
253
127
|
deriveLabelsOptions: { includeNativeLabel: true },
|
|
254
128
|
});
|
|
255
129
|
|
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
type PObjectId,
|
|
6
6
|
Annotation,
|
|
7
7
|
canonicalizeAxisId,
|
|
8
|
-
canonicalizeJson,
|
|
9
8
|
DiscoveredPColumnId,
|
|
10
|
-
getAxisId,
|
|
11
9
|
readAnnotation,
|
|
12
10
|
readAnnotationJson,
|
|
13
11
|
} from "@milaboratories/pl-model-common";
|
|
@@ -236,16 +234,13 @@ export type LabelableColumn = {
|
|
|
236
234
|
/** Derive labels for all table elements: columns via deriveDistinctLabels, axes from label columns. */
|
|
237
235
|
export function deriveAllLabels(options: {
|
|
238
236
|
columns: LabelableColumn[];
|
|
239
|
-
labelColumns: { readonly spec: PColumnSpec }[];
|
|
240
237
|
deriveLabelsOptions?: DeriveLabelsOptions;
|
|
241
238
|
}): Record<string, string> {
|
|
242
|
-
const { columns,
|
|
243
|
-
const axisLabels = deriveAxisLabels(columns, labelColumns);
|
|
239
|
+
const { columns, deriveLabelsOptions } = options;
|
|
244
240
|
const entries = columns.map((c) => ({
|
|
245
241
|
spec: c.spec,
|
|
246
242
|
linkerPath: c.linkerPath?.map((step) => ({
|
|
247
243
|
spec: step.linker.spec,
|
|
248
|
-
qualifications: step.qualifications,
|
|
249
244
|
})),
|
|
250
245
|
qualifications: c.qualifications,
|
|
251
246
|
}));
|
|
@@ -253,26 +248,7 @@ export function deriveAllLabels(options: {
|
|
|
253
248
|
(acc, label, index) => ((acc[columns[index].id] = label), acc),
|
|
254
249
|
{} as Record<string, string>,
|
|
255
250
|
);
|
|
256
|
-
return
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/** Derive axis labels from matching label columns, falling back to axis spec annotations. */
|
|
260
|
-
function deriveAxisLabels(
|
|
261
|
-
columns: { readonly spec: PColumnSpec }[],
|
|
262
|
-
labelColumns: { readonly spec: PColumnSpec }[],
|
|
263
|
-
): Record<string, string> {
|
|
264
|
-
const axisSpecMap = new Map(
|
|
265
|
-
columns.flatMap((c) => c.spec.axesSpec).map((a) => [canonicalizeJson(getAxisId(a)), a]),
|
|
266
|
-
);
|
|
267
|
-
const result: Record<string, string> = {};
|
|
268
|
-
for (const axisKey of axisSpecMap.keys()) {
|
|
269
|
-
const labelCol = labelColumns.find(
|
|
270
|
-
(lc) => canonicalizeJson(getAxisId(lc.spec.axesSpec[0])) === axisKey,
|
|
271
|
-
);
|
|
272
|
-
const source = labelCol?.spec ?? axisSpecMap.get(axisKey);
|
|
273
|
-
result[axisKey] = readAnnotation(source ?? {}, Annotation.Label)?.trim() ?? "Unlabeled";
|
|
274
|
-
}
|
|
275
|
-
return result;
|
|
251
|
+
return columnLabels;
|
|
276
252
|
}
|
|
277
253
|
|
|
278
254
|
/** Column shape required by tooltip derivation. */
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
Option,
|
|
4
|
-
PColumnSelector,
|
|
5
|
-
PObjectSpec,
|
|
6
|
-
} from "@milaboratories/pl-model-common";
|
|
1
|
+
import type { MultiColumnSelector, Option, PObjectSpec } from "@milaboratories/pl-model-common";
|
|
2
|
+
import { multiColumnSelectorsToPredicate } from "@milaboratories/pl-model-common";
|
|
7
3
|
import type { DeriveLabelsOptions } from "../../labels/derive_distinct_labels";
|
|
8
4
|
import type { RenderCtxBase } from "../../render";
|
|
9
5
|
import { ColumnCollectionBuilder } from "../../columns/column_collection_builder";
|
|
10
6
|
import { collectCtxColumnSnapshotProviders } from "../../columns/ctx_column_sources";
|
|
7
|
+
import type { DatasetOption } from "./dataset_selection";
|
|
11
8
|
import { buildRefMap, filterMatchesToOptions, findFilterColumns } from "./filter_discovery";
|
|
9
|
+
import { enrichmentVariantsToRefs, findEnrichmentColumns } from "./enrichment_discovery";
|
|
12
10
|
|
|
13
11
|
export type BuildDatasetOptions = {
|
|
14
12
|
/** Which result pool columns qualify as datasets. Defaults to all. */
|
|
15
|
-
|
|
13
|
+
primary?: MultiColumnSelector | MultiColumnSelector[] | ((spec: PObjectSpec) => boolean);
|
|
16
14
|
/** Formatting options for filter labels. */
|
|
17
15
|
labelOptions?: DeriveLabelsOptions;
|
|
16
|
+
/**
|
|
17
|
+
* Enables enrichment discovery and filters hits attached to
|
|
18
|
+
* `DatasetOption.enrichments`. Use `() => true` to accept all; omit to disable.
|
|
19
|
+
*/
|
|
20
|
+
withEnrichments?: MultiColumnSelector | MultiColumnSelector[] | ((spec: PObjectSpec) => boolean);
|
|
21
|
+
/** Maximum linker hops considered. Only used when `withEnrichments` is set. */
|
|
22
|
+
enrichmentMaxHops?: number;
|
|
18
23
|
};
|
|
19
24
|
|
|
20
25
|
/**
|
|
@@ -27,28 +32,54 @@ export function buildDatasetOptions(
|
|
|
27
32
|
ctx: RenderCtxBase,
|
|
28
33
|
opts?: BuildDatasetOptions,
|
|
29
34
|
): DatasetOption[] | undefined {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
35
|
+
const primary = opts?.primary;
|
|
36
|
+
const primaryPredicate =
|
|
37
|
+
primary === undefined
|
|
38
|
+
? () => true
|
|
39
|
+
: typeof primary === "function"
|
|
40
|
+
? primary
|
|
41
|
+
: multiColumnSelectorsToPredicate(primary);
|
|
42
|
+
const options = ctx.resultPool.getOptions(primaryPredicate, { refsWithEnrichments: true });
|
|
32
43
|
if (options.length === 0) return [];
|
|
33
44
|
|
|
34
45
|
const columnSources = collectCtxColumnSnapshotProviders(ctx);
|
|
35
46
|
const refMap = buildRefMap(ctx.resultPool.getSpecs().entries);
|
|
36
47
|
const pframeSpec = ctx.getService("pframeSpec");
|
|
37
48
|
|
|
38
|
-
return options.map((
|
|
39
|
-
const datasetSpec = ctx.resultPool.getPColumnSpecByRef(
|
|
40
|
-
if (!datasetSpec) return
|
|
49
|
+
return options.map((primary: Option): DatasetOption => {
|
|
50
|
+
const datasetSpec = ctx.resultPool.getPColumnSpecByRef(primary.ref);
|
|
51
|
+
if (!datasetSpec) return { primary };
|
|
41
52
|
|
|
42
53
|
const builder = new ColumnCollectionBuilder(pframeSpec);
|
|
43
54
|
for (const src of columnSources) builder.addSource(src);
|
|
44
55
|
const collection = builder.build({ anchors: { main: datasetSpec } });
|
|
45
|
-
if (!collection) return
|
|
56
|
+
if (!collection) return { primary };
|
|
46
57
|
|
|
47
58
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
const filterMatches = findFilterColumns(collection);
|
|
60
|
+
const filters =
|
|
61
|
+
filterMatches.length === 0
|
|
62
|
+
? undefined
|
|
63
|
+
: filterMatchesToOptions(filterMatches, refMap, opts?.labelOptions);
|
|
64
|
+
|
|
65
|
+
let enrichments;
|
|
66
|
+
if (opts?.withEnrichments !== undefined) {
|
|
67
|
+
const enrichmentVariants = findEnrichmentColumns(collection, {
|
|
68
|
+
maxHops: opts.enrichmentMaxHops,
|
|
69
|
+
...(typeof opts.withEnrichments === "function"
|
|
70
|
+
? { predicate: opts.withEnrichments }
|
|
71
|
+
: { include: opts.withEnrichments }),
|
|
72
|
+
});
|
|
73
|
+
if (enrichmentVariants.length > 0) {
|
|
74
|
+
enrichments = enrichmentVariantsToRefs(enrichmentVariants, opts.labelOptions);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
primary,
|
|
80
|
+
...(filters !== undefined && filters.length > 0 ? { filters } : {}),
|
|
81
|
+
...(enrichments !== undefined && enrichments.length > 0 ? { enrichments } : {}),
|
|
82
|
+
};
|
|
52
83
|
} finally {
|
|
53
84
|
collection.dispose();
|
|
54
85
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LabeledEnrichmentRefs, Option, PrimaryRef } from "@milaboratories/pl-model-common";
|
|
2
|
+
|
|
3
|
+
/** Dataset picker entry: user picks {@link primary}, gets {@link enrichments} attached. */
|
|
4
|
+
export type DatasetOption = {
|
|
5
|
+
readonly primary: Option;
|
|
6
|
+
readonly filters?: readonly Option[];
|
|
7
|
+
readonly enrichments?: LabeledEnrichmentRefs;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Picked dataset bundle emitted by `PlDatasetSelector`. Stored opaquely in
|
|
12
|
+
* block data; block authors unbundle inside their args resolver.
|
|
13
|
+
*/
|
|
14
|
+
export type DatasetSelection = {
|
|
15
|
+
readonly __isDatasetSelection: "v1";
|
|
16
|
+
readonly primary: PrimaryRef;
|
|
17
|
+
readonly enrichments?: LabeledEnrichmentRefs;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function isDatasetSelection(value: unknown): value is DatasetSelection {
|
|
21
|
+
return (
|
|
22
|
+
typeof value === "object" &&
|
|
23
|
+
value !== null &&
|
|
24
|
+
(value as { __isDatasetSelection?: unknown }).__isDatasetSelection === "v1" &&
|
|
25
|
+
"primary" in value
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createDatasetSelection(
|
|
30
|
+
primary: PrimaryRef,
|
|
31
|
+
enrichments?: LabeledEnrichmentRefs,
|
|
32
|
+
): DatasetSelection {
|
|
33
|
+
if (enrichments !== undefined && enrichments.length > 0) {
|
|
34
|
+
return { __isDatasetSelection: "v1", primary, enrichments };
|
|
35
|
+
}
|
|
36
|
+
return { __isDatasetSelection: "v1", primary };
|
|
37
|
+
}
|