Skip to content

Commit 4606fe5

Browse files
authored
Offline-review findings: table sorting, settings, labels, histogram titles (#8)
* Rename Integrity risk column to Structural liabilities Aligns the structural-integrity column with the Antibody Sequence Liabilities block: same None/Present semantics, now styled and ranked as a score. The PColumn spec name is unchanged so downstream Lead Selection joins keep resolving it. * Fix results-table sorting and simplify block settings Sorting now works: the results-table state is persisted in BlockData and passed to createPlDataTableV3, so AG-Grid sorts re-derive rows server-side instead of leaving placeholder cells. A v1 to v2 data migration seeds the new tableState and drops the now-removed manual heavy/light chain inputs, which are auto-detected from the structure. Also rewrites the advanced confidence-threshold tooltips to lead with what changing them does. * Render the histogram chart title via graph-maker Histogram pages stay in a PlBlockPage with no-body-gutters and no page heading; the page label is carried by graph-maker's own chart title, which is part of its v-model state. inheritAttrs is disabled so the config title spread onto the component cannot also render as a PlBlockPage heading. * Add changeset for the offline-review fixes Bumps the block so the corrected long description republishes: the central registry currently serves a stale generic description because the last docs edit shipped without a version bump.
1 parent 39e2b49 commit 4606fe5

8 files changed

Lines changed: 82 additions & 77 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@platforma-open/milaboratories.3d-structure-based-liabilities": patch
3+
"@platforma-open/milaboratories.3d-structure-based-liabilities.model": patch
4+
"@platforma-open/milaboratories.3d-structure-based-liabilities.ui": patch
5+
"@platforma-open/milaboratories.3d-structure-based-liabilities.workflow": patch
6+
---
7+
8+
Fix results-table sorting, remove the manual heavy/light chain inputs (now auto-detected), clarify the advanced threshold tooltips, rename the "Integrity risk" column to "Structural liabilities", and restore the histogram page titles.

model/src/index.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,35 @@ import {
1414
ArrayColumnProvider,
1515
BlockModelV3,
1616
buildDatasetOptions,
17+
createPlDataTableStateV2,
1718
createPlDataTableV3,
1819
DataModelBuilder,
1920
getAxisId,
2021
isPColumnSpec,
2122
parseResourceMap,
2223
} from "@platforma-sdk/model";
23-
import type { BlockArgs, BlockData, DetectedMode } from "./types";
24+
import type { BlockArgs, BlockData, BlockDataV1, DetectedMode } from "./types";
2425

2526
export type { NumberingScheme, DetectedMode, BlockData, BlockArgs } from "./types";
2627

27-
const dataModel = new DataModelBuilder().from<BlockData>("v1").init(() => ({
28-
dataset: undefined,
29-
heavyChainId: "",
30-
lightChainId: "",
31-
frConfThresh: 4.0,
32-
cdrConfThresh: 6.0,
33-
customBlockLabel: "",
34-
}));
28+
const dataModel = new DataModelBuilder()
29+
.from<BlockDataV1>("v1")
30+
// v1 -> v2: drop the removed manual chain fields and seed the persisted
31+
// results-table state so existing block instances gain sortable tables.
32+
.migrate<BlockData>("v2", (v1) => ({
33+
dataset: v1.dataset,
34+
frConfThresh: v1.frConfThresh,
35+
cdrConfThresh: v1.cdrConfThresh,
36+
customBlockLabel: v1.customBlockLabel,
37+
tableState: createPlDataTableStateV2(),
38+
}))
39+
.init(() => ({
40+
dataset: undefined,
41+
frConfThresh: 4.0,
42+
cdrConfThresh: 6.0,
43+
customBlockLabel: "",
44+
tableState: createPlDataTableStateV2(),
45+
}));
3546

3647
type ScoresCtx = BlockRenderCtx<unknown, unknown>;
3748
type ScoresPColumn = PColumn<PColumnDataUniversal | undefined>;
@@ -105,8 +116,6 @@ export const platforma = BlockModelV3.create(dataModel)
105116
}
106117
return {
107118
primaryRef: data.dataset.primary,
108-
heavyChainId: data.heavyChainId,
109-
lightChainId: data.lightChainId,
110119
frConfThresh: data.frConfThresh,
111120
cdrConfThresh: data.cdrConfThresh,
112121
};
@@ -158,6 +167,7 @@ export const platforma = BlockModelV3.create(dataModel)
158167
const mode = resolveMode(ctx);
159168
return createPlDataTableV3(ctx, {
160169
columns: variants,
170+
tableState: ctx.data.tableState,
161171
displayOptions: mode
162172
? {
163173
visibility: [

model/src/types.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DatasetSelection } from "@platforma-sdk/model";
1+
import type { DatasetSelection, PlDataTableStateV2 } from "@platforma-sdk/model";
22

33
/** Numbering schemes the runtime can interpret. The block is now hardcoded
44
* to IMGT at the workflow layer because every supported upstream emits
@@ -13,22 +13,32 @@ export type DetectedMode = "TAP" | "TNP";
1313
export type BlockData = {
1414
/** Predicted-structures dataset picked via `PlDatasetSelector`. */
1515
dataset?: DatasetSelection;
16-
/** Manual heavy/light chain mapping, used only when a PDB carries no
17-
* REMARK 99 PLATFORMA CDR records to auto-detect them. */
18-
heavyChainId: string;
19-
lightChainId: string;
2016
/** Confidence-gating thresholds (Å) for framework and CDR regions. */
2117
frConfThresh: number;
2218
cdrConfThresh: number;
2319
/** User-set block label; empty falls back to the derived default. */
2420
customBlockLabel: string;
21+
/** Results-table sort / filter / column state. Persisted in the model and
22+
* fed into `createPlDataTableV3` so sorting re-derives rows server-side
23+
* instead of leaving AG-Grid with unsortable placeholder cells. */
24+
tableState: PlDataTableStateV2;
25+
};
26+
27+
/** Pre-v2 shape: carried manual `heavyChainId`/`lightChainId` (now
28+
* auto-detected, removed) and no persisted `tableState`. Kept so the v1 -> v2
29+
* migration can map existing block instances onto the current shape. */
30+
export type BlockDataV1 = {
31+
dataset?: DatasetSelection;
32+
heavyChainId: string;
33+
lightChainId: string;
34+
frConfThresh: number;
35+
cdrConfThresh: number;
36+
customBlockLabel: string;
2537
};
2638

2739
/** Projection consumed by the workflow. */
2840
export type BlockArgs = {
2941
primaryRef: NonNullable<DatasetSelection["primary"]>;
30-
heavyChainId: string;
31-
lightChainId: string;
3242
frConfThresh: number;
3343
cdrConfThresh: number;
3444
};

ui/src/components/HistogramPage.vue

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@ import { PlBlockPage } from "@platforma-sdk/ui-vue";
66
import { computed } from "vue";
77
import type { ThresholdBands } from "../pages/histogramConfigs";
88
9+
// The page wrappers spread the whole config; keys that aren't declared props
10+
// (title / columnName / fillColor) feed makeGraphState, not this component.
11+
// Drop them rather than let `title` fall through to PlBlockPage as a second
12+
// page heading: graph-maker's own title (from its v-model state) is the only
13+
// title we want.
14+
defineOptions({ inheritAttrs: false });
15+
916
const props = defineProps<{
10-
// No page heading rendered. The section nav already labels the page;
11-
// the threshold legend lives inside graph-maker's titleLineSlot so it
12-
// sits next to the chart title bar instead of pushing the chart down.
17+
// No PlBlockPage title: the chart's own title (graph-maker v-model state)
18+
// carries the page label. Threshold lines are drawn by graph-maker from the
19+
// value column's `pl7.app/graph/thresholds` annotation; the legend below
20+
// maps the bands those lines delimit.
1321
notReadyTitle?: string;
1422
thresholds?: ThresholdBands;
1523
pFrame: OutputWithStatus<PFrameHandle>;
@@ -36,7 +44,7 @@ const hasLegend = computed(() => {
3644
</script>
3745

3846
<template>
39-
<PlBlockPage>
47+
<PlBlockPage no-body-gutters>
4048
<GraphMaker
4149
v-model="graphStateModel"
4250
chart-type="histogram"

ui/src/pages/MainPage.vue

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import type { PlStructureViewerProps } from "@milaboratories/structure-viewer";
33
import { PlStructureViewer } from "@milaboratories/structure-viewer";
44
import type { PFrameHandle, PTableKey } from "@platforma-sdk/model";
5-
import { createPlDataTableStateV2 } from "@platforma-sdk/model";
65
import { defaultBlockLabelFor } from "@platforma-open/milaboratories.3d-structure-based-liabilities.model";
76
import {
87
PlAccordionSection,
@@ -14,7 +13,6 @@ import {
1413
PlMaskIcon24,
1514
PlNumberField,
1615
PlSlideModal,
17-
PlTextField,
1816
usePlDataTableSettingsV2,
1917
} from "@platforma-sdk/ui-vue";
2018
import { computed, ref } from "vue";
@@ -33,9 +31,6 @@ const scoresTableSettings = usePlDataTableSettingsV2({
3331
model: () => app.model.outputs.scoresTable,
3432
sourceId: () => "scores-v2",
3533
});
36-
// v-model writes stay UI-local so AG-Grid state events don't re-fire the
37-
// model output handler.
38-
const scoresLocalState = ref(createPlDataTableStateV2());
3934
4035
const pdbsMap = computed(() => app.model.outputs.clonotypePdbsMap);
4136
const clonotypeAxisId = computed(() => app.model.outputs.clonotypeAxisId);
@@ -111,30 +106,6 @@ const modalTitle = computed(() => {
111106
clearable
112107
/>
113108

114-
<div class="field-grid field-grid--settings">
115-
<PlTextField
116-
v-model="app.model.data.heavyChainId"
117-
label="Heavy chain"
118-
placeholder="auto-detect"
119-
>
120-
<template #tooltip>
121-
Which chain in the structure is the heavy chain. Leave empty to detect it automatically;
122-
set a single chain letter (e.g. A) only if a structure has no chain annotation and
123-
detection fails.
124-
</template>
125-
</PlTextField>
126-
<PlTextField
127-
v-model="app.model.data.lightChainId"
128-
label="Light chain"
129-
placeholder="auto-detect"
130-
>
131-
<template #tooltip>
132-
Which chain in the structure is the light chain. Leave empty to detect it automatically;
133-
set a single chain letter only if detection fails.
134-
</template>
135-
</PlTextField>
136-
</div>
137-
138109
<PlAccordionSection label="Advanced thresholds">
139110
<div class="field-grid">
140111
<PlNumberField
@@ -145,9 +116,11 @@ const modalTitle = computed(() => {
145116
:step="0.5"
146117
>
147118
<template #tooltip>
148-
Predicted-error cutoff above which framework-region motifs are treated as too
149-
uncertain to flag. Raise it for experimental crystal structures whose B-factors are
150-
temperature factors rather than predicted error.
119+
Framework-region residues whose predicted error exceeds this cutoff (Å) are dropped
120+
before scoring, so their motifs are not flagged. Higher keeps and flags more residues,
121+
including uncertain ones; lower trusts only high-confidence framework. Default 4 Å.
122+
Raise it for experimental crystal structures, whose B-factors are temperature factors
123+
rather than predicted error.
151124
</template>
152125
</PlNumberField>
153126
<PlNumberField
@@ -158,8 +131,10 @@ const modalTitle = computed(() => {
158131
:step="0.5"
159132
>
160133
<template #tooltip>
161-
Same as the framework threshold, applied to the more flexible CDR loops, where
162-
predicted structures are typically less certain.
134+
Same gating as the framework threshold, applied to the CDR loops. CDRs are more
135+
flexible and usually predicted with lower confidence, so this cutoff is looser by
136+
default (6 Å). Higher flags motifs in more CDR residues; lower keeps only
137+
high-confidence CDR positions.
163138
</template>
164139
</PlNumberField>
165140
</div>
@@ -184,7 +159,7 @@ const modalTitle = computed(() => {
184159
<!-- One row per clonotype. The open button on the clonotype-key cell pops
185160
the structure-viewer modal for that row. -->
186161
<PlAgDataTableV2
187-
v-model="scoresLocalState"
162+
v-model="app.model.data.tableState"
188163
:settings="scoresTableSettings"
189164
:show-cell-button-for-axis-id="clonotypeAxisId"
190165
:cell-button-invoke-rows-on-double-click="true"
@@ -230,10 +205,6 @@ const modalTitle = computed(() => {
230205
gap: 12px;
231206
margin-bottom: 8px;
232207
}
233-
.field-grid--settings {
234-
margin-top: 12px;
235-
}
236-
237208
.run-alert {
238209
margin-top: 12px;
239210
}

ui/src/pages/histogramConfigs.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ export const histogramConfigs = {
9494

9595
/** Seed a graph-maker `bins` template from a histogram config. The `bins`
9696
* layer needs an explicit fillColor; the template's default ('white') is
97-
* invisible against the chart background. The chart title is left blank
98-
* because the section nav already labels the page; rendering `cfg.title`
99-
* here too duplicates it inside the chart frame. */
97+
* invisible against the chart background. `title` is graph-maker's own chart
98+
* title (part of its v-model state, editable in the chart), which carries the
99+
* page label since the page renders no PlBlockPage heading of its own. */
100100
export function makeGraphState(cfg: HistogramConfig): GraphMakerState {
101101
return {
102102
template: "bins",
103-
title: "",
103+
title: cfg.title,
104104
currentTab: null,
105105
layersSettings: { bins: { fillColor: cfg.fillColor } },
106106
};

workflow/src/main.tpl.tengo

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,12 @@ wf.body(func(args) {
116116
arg("--clonotype-filter").arg("clonotype_filter.tsv")
117117
}
118118

119-
// Empty strings fall through to the Python defaults.
120-
withOptArg := func(c, flag, value) {
121-
if is_undefined(value) || value == "" {
122-
return c
123-
}
124-
return c.arg(flag).arg(value)
125-
}
126119
// Numbering scheme is hardcoded to IMGT: every supported upstream
127120
// (3D Structure Prediction block, ImmuneBuilder predictions) emits
128121
// IMGT-numbered structures, and our compactness anchors + canonical
129-
// disulfide positions assume IMGT.
122+
// disulfide positions assume IMGT. Heavy/light chains are auto-detected
123+
// from the structure's REMARK records, so no chain flags are passed.
130124
cmd = cmd.arg("--numbering-scheme").arg("imgt")
131-
cmd = withOptArg(cmd, "--chain-h", args.heavyChainId)
132-
cmd = withOptArg(cmd, "--chain-l", args.lightChainId)
133125

134126
result := cmd.
135127
saveFile("per_clonotype.tsv").

workflow/src/specs.lib.tengo

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,20 @@ scoresColumnSpecs := {
211211
"pl7.app/table/orderPriority": "1050"
212212
}
213213
},
214+
// Renamed from "Integrity risk" to match the "Structural liabilities"
215+
// column in the Antibody Sequence Liabilities block: same None/Present
216+
// semantics, styled and ranked as a score. The spec `name` is unchanged
217+
// so downstream joins (Lead Selection) keep resolving it.
214218
structuralIntegrityRisk: {
215219
valueType: "String",
216220
name: "pl7.app/liabilities/structuralIntegrityRisk",
217221
annotations: {
218-
"pl7.app/label": "Integrity risk",
219-
"pl7.app/description": "Binary flag (None/Present) for hard-to-fix structural issues: broken/missing disulfides, exposed extra Cys, structural motifs.",
222+
"pl7.app/label": "Structural liabilities",
223+
"pl7.app/description": "Binary flag (None/Present) for hard-to-fix structural issues: broken/missing disulfides, exposed extra Cys, structural motifs. These require scaffold redesign rather than point substitutions.",
224+
"pl7.app/isScore": "true",
220225
"pl7.app/isDiscreteFilter": "true",
221226
"pl7.app/discreteValues": INTEGRITY_DISCRETE_VALUES,
227+
"pl7.app/score/rankingOrder": "decreasing",
222228
"pl7.app/table/visibility": "default",
223229
"pl7.app/table/orderPriority": "1040"
224230
}

0 commit comments

Comments
 (0)