@platforma-open/milaboratories.top-antibodies.workflow 4.0.3 → 4.1.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/.turbo/turbo-build.log +3 -1
- package/CHANGELOG.md +10 -0
- package/dist/tengo/lib/selection-stage-conv.lib.tengo +33 -0
- package/dist/tengo/lib/utils.lib.tengo +26 -0
- package/dist/tengo/tpl/assembling-fasta.plj.gz +0 -0
- package/dist/tengo/tpl/filter-and-sample.plj.gz +0 -0
- package/dist/tengo/tpl/main.plj.gz +0 -0
- package/package.json +3 -3
- package/src/filter-and-sample.tpl.tengo +45 -9
- package/src/main.tpl.tengo +6 -0
- package/src/selection-stage-conv.lib.tengo +33 -0
- package/src/utils.lib.tengo +26 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
WARN Issue while reading "/home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
|
|
2
2
|
|
|
3
|
-
> @platforma-open/milaboratories.top-antibodies.workflow@4.0
|
|
3
|
+
> @platforma-open/milaboratories.top-antibodies.workflow@4.1.0 build /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow
|
|
4
4
|
> shx rm -rf dist && pl-tengo check && pl-tengo build
|
|
5
5
|
|
|
6
6
|
Processing "src/assembling-fasta.tpl.tengo"...
|
|
@@ -11,6 +11,7 @@ Processing "src/pf-spectratype-conv.lib.tengo"...
|
|
|
11
11
|
Processing "src/pf-vj-usage-conv.lib.tengo"...
|
|
12
12
|
Processing "src/sampled-cols-conv.lib.tengo"...
|
|
13
13
|
Processing "src/sampled-export-conv.lib.tengo"...
|
|
14
|
+
Processing "src/selection-stage-conv.lib.tengo"...
|
|
14
15
|
Processing "src/utils.lib.tengo"...
|
|
15
16
|
No syntax errors found.
|
|
16
17
|
info: Compiling 'dist'...
|
|
@@ -19,6 +20,7 @@ No syntax errors found.
|
|
|
19
20
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/pf-vj-usage-conv.lib.tengo
|
|
20
21
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/sampled-cols-conv.lib.tengo
|
|
21
22
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/sampled-export-conv.lib.tengo
|
|
23
|
+
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/selection-stage-conv.lib.tengo
|
|
22
24
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/utils.lib.tengo
|
|
23
25
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/tpl/assembling-fasta.plj.gz
|
|
24
26
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/tpl/filter-and-sample.plj.gz
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @platforma-open/milaboratories.top-antibodies.workflow
|
|
2
2
|
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b812c7d: Track which filter step eliminated each clonotype (or marks it as a
|
|
8
|
+
survivor) and visualize the attrition in a new Selection page. The
|
|
9
|
+
sample-clonotypes script emits a selectionStage column per clone; the
|
|
10
|
+
workflow exposes it as selectionStagePf, and the block UI renders it
|
|
11
|
+
via GraphMaker's selection chart type.
|
|
12
|
+
|
|
3
13
|
## 4.0.3
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
json := import("json")
|
|
3
|
+
|
|
4
|
+
getColumns := func(datasetSpec, valueLabels) {
|
|
5
|
+
return {
|
|
6
|
+
axes: [
|
|
7
|
+
{
|
|
8
|
+
column: "clonotypeKey",
|
|
9
|
+
spec: datasetSpec.axesSpec[1]
|
|
10
|
+
}],
|
|
11
|
+
columns: [{
|
|
12
|
+
column: "selectionStage",
|
|
13
|
+
id: "selectionStage",
|
|
14
|
+
allowNA: false,
|
|
15
|
+
spec: {
|
|
16
|
+
name: "pl7.app/selectionStage",
|
|
17
|
+
valueType: "Int",
|
|
18
|
+
domain: {},
|
|
19
|
+
annotations: {
|
|
20
|
+
"pl7.app/label": "Selection Stage",
|
|
21
|
+
"pl7.app/valueLabels": string(json.encode(valueLabels)),
|
|
22
|
+
"pl7.app/isDiscreteFilter": "true"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}],
|
|
26
|
+
storageFormat: "Parquet",
|
|
27
|
+
partitionKeyLength: 0
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export ll.toStrict({
|
|
32
|
+
getColumns: getColumns
|
|
33
|
+
})
|
|
@@ -742,6 +742,31 @@ buildFilterTraceLabel := func(filters, columns) {
|
|
|
742
742
|
return result
|
|
743
743
|
}
|
|
744
744
|
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
buildFilterStageNames := func(filters, columns) {
|
|
754
|
+
names := []
|
|
755
|
+
for filter in filters {
|
|
756
|
+
|
|
757
|
+
if !is_undefined(filter.value) && !is_undefined(columns.getColumn(filter.value.column).spec) {
|
|
758
|
+
colSpec := columns.getColumn(filter.value.column).spec
|
|
759
|
+
label := colSpec.name
|
|
760
|
+
colLabel := colSpec.annotations["pl7.app/label"]
|
|
761
|
+
if !is_undefined(colLabel) && colLabel != "" {
|
|
762
|
+
label = colLabel
|
|
763
|
+
}
|
|
764
|
+
names = names + [label]
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return names
|
|
768
|
+
}
|
|
769
|
+
|
|
745
770
|
export {
|
|
746
771
|
clusterAxisDomainsMatch: clusterAxisDomainsMatch,
|
|
747
772
|
findMatchingLinkerIndex: findMatchingLinkerIndex,
|
|
@@ -755,5 +780,6 @@ export {
|
|
|
755
780
|
initializeAssemSeqTable: initializeAssemSeqTable,
|
|
756
781
|
formatFilterDescription: formatFilterDescription,
|
|
757
782
|
buildFilterTraceLabel: buildFilterTraceLabel,
|
|
783
|
+
buildFilterStageNames: buildFilterStageNames,
|
|
758
784
|
inVivoScoreSourceColumns: inVivoScoreSourceColumns
|
|
759
785
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-open/milaboratories.top-antibodies.workflow",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Block Workflow",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@platforma-sdk/workflow-tengo": "5.21.0",
|
|
8
8
|
"@platforma-open/milaboratories.software-anarci": "^0.0.3",
|
|
9
9
|
"@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "2.1.3",
|
|
10
|
+
"@platforma-open/milaboratories.top-antibodies.assembling-fasta": "1.3.3",
|
|
10
11
|
"@platforma-open/milaboratories.top-antibodies.spectratype": "1.8.4",
|
|
11
12
|
"@platforma-open/milaboratories.top-antibodies.umap": "1.2.4",
|
|
12
|
-
"@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.4.4"
|
|
13
|
-
"@platforma-open/milaboratories.top-antibodies.assembling-fasta": "1.3.3"
|
|
13
|
+
"@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.4.4"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@platforma-sdk/tengo-builder": "2.5.26"
|
|
@@ -8,9 +8,10 @@ pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
|
|
|
8
8
|
render := import("@platforma-sdk/workflow-tengo:render")
|
|
9
9
|
sampledColsConv := import(":sampled-cols-conv")
|
|
10
10
|
sampledExportConv := import(":sampled-export-conv")
|
|
11
|
+
selectionStageConv := import(":selection-stage-conv")
|
|
11
12
|
json := import("json")
|
|
12
13
|
|
|
13
|
-
self.defineOutputs("sampledRows", "finalClonotypes", "sampledColumnsExport")
|
|
14
|
+
self.defineOutputs("sampledRows", "finalClonotypes", "sampledColumnsExport", "selectionStagePf")
|
|
14
15
|
|
|
15
16
|
self.body(func(inputs) {
|
|
16
17
|
|
|
@@ -22,8 +23,8 @@ self.body(func(inputs) {
|
|
|
22
23
|
|
|
23
24
|
outputs := {}
|
|
24
25
|
finalClonotypes := undefined
|
|
25
|
-
|
|
26
|
-
// Run filtering script
|
|
26
|
+
|
|
27
|
+
// Run filtering script with selection stage tracking
|
|
27
28
|
filterResult := exec.builder().
|
|
28
29
|
software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.sample-clonotypes:filter")).
|
|
29
30
|
mem("16GiB").
|
|
@@ -32,13 +33,16 @@ self.body(func(inputs) {
|
|
|
32
33
|
arg("--parquet").arg("clonotypes.parquet").
|
|
33
34
|
arg("--out").arg("filteredClonotypes.parquet").
|
|
34
35
|
arg("--filter-map").arg(string(json.encode(filterMap))).
|
|
36
|
+
arg("--emit-selection").arg("selection.parquet").
|
|
35
37
|
saveFile("filteredClonotypes.parquet").
|
|
38
|
+
saveFile("selection.parquet").
|
|
36
39
|
printErrStreamToStdout().
|
|
37
40
|
cache(24 * 60 * 60 * 1000).
|
|
38
41
|
run()
|
|
39
42
|
|
|
40
|
-
// Save filtered parquet file
|
|
43
|
+
// Save filtered parquet file and selection stage data
|
|
41
44
|
filteredClonotypes := filterResult.getFile("filteredClonotypes.parquet")
|
|
45
|
+
selectionParquet := filterResult.getFile("selection.parquet")
|
|
42
46
|
|
|
43
47
|
// Check if In Vivo Score is in the ranking map
|
|
44
48
|
hasInVivoScore := false
|
|
@@ -60,15 +64,18 @@ self.body(func(inputs) {
|
|
|
60
64
|
if topClonotypes != undefined {
|
|
61
65
|
|
|
62
66
|
////////// Top Clonotypes Sampling //////////
|
|
63
|
-
// Run sampling script on filtered data
|
|
67
|
+
// Run sampling script on filtered data, with selection stage merging
|
|
64
68
|
builder := exec.builder().
|
|
65
69
|
software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.sample-clonotypes:main")).
|
|
66
70
|
mem("16GiB").
|
|
67
71
|
cpu(1).
|
|
68
72
|
addFile("filteredClonotypes.parquet", filteredClonotypes).
|
|
73
|
+
addFile("selection_in.parquet", selectionParquet).
|
|
69
74
|
arg("--parquet").arg("filteredClonotypes.parquet").
|
|
70
75
|
arg("--n").arg(string(topClonotypes)).
|
|
71
|
-
arg("--ranking-map").arg(string(json.encode(rankingMap)))
|
|
76
|
+
arg("--ranking-map").arg(string(json.encode(rankingMap))).
|
|
77
|
+
arg("--selection-in").arg("selection_in.parquet").
|
|
78
|
+
arg("--selection-out").arg("selection_out.parquet")
|
|
72
79
|
|
|
73
80
|
// Add diversification column if provided
|
|
74
81
|
if inputs.diversificationColumn != undefined && inputs.diversificationColumn != "" {
|
|
@@ -78,29 +85,57 @@ self.body(func(inputs) {
|
|
|
78
85
|
sampleClones := builder.
|
|
79
86
|
arg("--out").arg("sampledClonotypes_top.parquet").
|
|
80
87
|
saveFile("sampledClonotypes_top.parquet").
|
|
88
|
+
saveFile("selection_out.parquet").
|
|
81
89
|
printErrStreamToStdout().
|
|
82
90
|
cache(24 * 60 * 60 * 1000).
|
|
83
91
|
run()
|
|
84
92
|
|
|
85
93
|
// Save top clonotypes parquet file
|
|
86
94
|
finalClonotypes = sampleClones.getFile("sampledClonotypes_top.parquet")
|
|
87
|
-
|
|
95
|
+
// Updated selection stages with "Selected" stage
|
|
96
|
+
selectionParquet = sampleClones.getFile("selection_out.parquet")
|
|
97
|
+
|
|
88
98
|
// Store outputs
|
|
89
99
|
sampledColsParams := sampledColsConv.getColumns(datasetSpec, true, hasInVivoScore) // Add ranking column
|
|
90
100
|
sampledColumnsPf := xsv.importFile(finalClonotypes, "parquet", sampledColsParams,
|
|
91
101
|
{cpu: 1, mem: "16GiB"})
|
|
92
102
|
outputs["sampledRows"] = pframes.exportFrame(sampledColumnsPf)
|
|
93
|
-
}
|
|
103
|
+
}
|
|
94
104
|
|
|
95
105
|
outputs["finalClonotypes"] = finalClonotypes
|
|
96
106
|
|
|
107
|
+
// Build valueLabels for selectionStage from filter stage names
|
|
108
|
+
filterStageNames := inputs.filterStageNames
|
|
109
|
+
valueLabels := {}
|
|
110
|
+
stageIdx := 1
|
|
111
|
+
if !is_undefined(filterStageNames) {
|
|
112
|
+
for name in filterStageNames {
|
|
113
|
+
valueLabels[string(stageIdx)] = name
|
|
114
|
+
stageIdx = stageIdx + 1
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if topClonotypes != undefined {
|
|
119
|
+
valueLabels[string(stageIdx)] = "Filtered"
|
|
120
|
+
valueLabels[string(stageIdx + 1)] = "Selected"
|
|
121
|
+
} else {
|
|
122
|
+
valueLabels[string(stageIdx)] = "Passed Filters"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Import selection stage parquet as selectionStage PColumn
|
|
126
|
+
selectionStageParams := selectionStageConv.getColumns(datasetSpec, valueLabels)
|
|
127
|
+
selectionStagePf := xsv.importFile(selectionParquet, "parquet", selectionStageParams,
|
|
128
|
+
{cpu: 1, mem: "16GiB"})
|
|
129
|
+
// Expose as a dedicated output for the Selection Plot page's pre-bound pframe.
|
|
130
|
+
outputs["selectionStagePf"] = pframes.exportFrame(selectionStagePf)
|
|
131
|
+
|
|
97
132
|
// Export only the sampling column to be used as a filter downstream
|
|
98
133
|
sampledExportsParams := sampledExportConv.getColumns(datasetSpec, filterMap,
|
|
99
134
|
rankingMap, inputs.diversificationColumn, topClonotypes)
|
|
100
135
|
sampledColumnsOnlyPf := xsv.importFile(finalClonotypes, "parquet", sampledExportsParams,
|
|
101
136
|
{cpu: 1, mem: "16GiB"})
|
|
102
137
|
|
|
103
|
-
// Create trace with filter descriptions and inject into exported
|
|
138
|
+
// Create trace with filter descriptions and inject into exported columns
|
|
104
139
|
traceLabel := inputs.filterTraceLabel
|
|
105
140
|
if is_undefined(traceLabel) || traceLabel == "" {
|
|
106
141
|
traceLabel = "Selected Leads"
|
|
@@ -117,6 +152,7 @@ self.body(func(inputs) {
|
|
|
117
152
|
})
|
|
118
153
|
exportPf := pframes.pFrameBuilder()
|
|
119
154
|
exportPf.add("link", trace.inject(sampledColumnsOnlyPf["link.spec"]), sampledColumnsOnlyPf["link.data"])
|
|
155
|
+
exportPf.add("selectionStage", trace.inject(selectionStagePf["selectionStage.spec"]), selectionStagePf["selectionStage.data"])
|
|
120
156
|
outputs["sampledColumnsExport"] = exportPf.build()
|
|
121
157
|
|
|
122
158
|
return outputs
|
package/src/main.tpl.tengo
CHANGED
|
@@ -180,6 +180,8 @@ wf.body(func(args) {
|
|
|
180
180
|
|
|
181
181
|
// Build trace label from filter descriptions
|
|
182
182
|
filterTraceLabel := utils.buildFilterTraceLabel(args.filters, columns)
|
|
183
|
+
// Build per-filter stage names for Selection Plot
|
|
184
|
+
filterStageNames := utils.buildFilterStageNames(args.filters, columns)
|
|
183
185
|
|
|
184
186
|
// Use ender.create to call the filter-clonotypes template
|
|
185
187
|
filterSampleResult := render.create(filterAndSampleTpl, {
|
|
@@ -193,6 +195,7 @@ wf.body(func(args) {
|
|
|
193
195
|
topClonotypes: args.topClonotypes,
|
|
194
196
|
diversificationColumn: clusterColumnHeader,
|
|
195
197
|
filterTraceLabel: filterTraceLabel,
|
|
198
|
+
filterStageNames: filterStageNames,
|
|
196
199
|
customBlockLabel: args.customBlockLabel
|
|
197
200
|
})
|
|
198
201
|
|
|
@@ -205,6 +208,9 @@ wf.body(func(args) {
|
|
|
205
208
|
// Export only the sampling column for downstream (built inside sub-template to avoid hang)
|
|
206
209
|
exports["sampledColumnsPf"] = filterSampleResult.output("sampledColumnsExport", 24 * 60 * 60 * 1000)
|
|
207
210
|
|
|
211
|
+
// Selection stage pframe for the Selection Plot page's pre-bound input
|
|
212
|
+
outputs["selectionStagePf"] = filterSampleResult.output("selectionStagePf", 24 * 60 * 60 * 1000)
|
|
213
|
+
|
|
208
214
|
// CDR3 spectratype, V/J gene usage, and Kabat numbering are VDJ-only — skip for peptide inputs.
|
|
209
215
|
if !isPeptide {
|
|
210
216
|
////////// CDR3 Length Calculation //////////
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
json := import("json")
|
|
3
|
+
|
|
4
|
+
getColumns := func(datasetSpec, valueLabels) {
|
|
5
|
+
return {
|
|
6
|
+
axes: [
|
|
7
|
+
{
|
|
8
|
+
column: "clonotypeKey",
|
|
9
|
+
spec: datasetSpec.axesSpec[1]
|
|
10
|
+
}],
|
|
11
|
+
columns: [{
|
|
12
|
+
column: "selectionStage",
|
|
13
|
+
id: "selectionStage",
|
|
14
|
+
allowNA: false,
|
|
15
|
+
spec: {
|
|
16
|
+
name: "pl7.app/selectionStage",
|
|
17
|
+
valueType: "Int",
|
|
18
|
+
domain: {},
|
|
19
|
+
annotations: {
|
|
20
|
+
"pl7.app/label": "Selection Stage",
|
|
21
|
+
"pl7.app/valueLabels": string(json.encode(valueLabels)),
|
|
22
|
+
"pl7.app/isDiscreteFilter": "true"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}],
|
|
26
|
+
storageFormat: "Parquet",
|
|
27
|
+
partitionKeyLength: 0
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export ll.toStrict({
|
|
32
|
+
getColumns: getColumns
|
|
33
|
+
})
|
package/src/utils.lib.tengo
CHANGED
|
@@ -742,6 +742,31 @@ buildFilterTraceLabel := func(filters, columns) {
|
|
|
742
742
|
return result
|
|
743
743
|
}
|
|
744
744
|
|
|
745
|
+
/**
|
|
746
|
+
* Builds an ordered list of filter stage names for the Selection Plot valueLabels.
|
|
747
|
+
* Returns one label per filter, in the same order filters are applied.
|
|
748
|
+
*
|
|
749
|
+
* @param filters - Array of filter objects from block args
|
|
750
|
+
* @param columns - PBundle to look up column specs for labels
|
|
751
|
+
* @return Array of strings, one per filter stage
|
|
752
|
+
*/
|
|
753
|
+
buildFilterStageNames := func(filters, columns) {
|
|
754
|
+
names := []
|
|
755
|
+
for filter in filters {
|
|
756
|
+
// Same guard as initializeCloneTable: skip filters with missing upstream columns
|
|
757
|
+
if !is_undefined(filter.value) && !is_undefined(columns.getColumn(filter.value.column).spec) {
|
|
758
|
+
colSpec := columns.getColumn(filter.value.column).spec
|
|
759
|
+
label := colSpec.name
|
|
760
|
+
colLabel := colSpec.annotations["pl7.app/label"]
|
|
761
|
+
if !is_undefined(colLabel) && colLabel != "" {
|
|
762
|
+
label = colLabel
|
|
763
|
+
}
|
|
764
|
+
names = names + [label]
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return names
|
|
768
|
+
}
|
|
769
|
+
|
|
745
770
|
export {
|
|
746
771
|
clusterAxisDomainsMatch: clusterAxisDomainsMatch,
|
|
747
772
|
findMatchingLinkerIndex: findMatchingLinkerIndex,
|
|
@@ -755,5 +780,6 @@ export {
|
|
|
755
780
|
initializeAssemSeqTable: initializeAssemSeqTable,
|
|
756
781
|
formatFilterDescription: formatFilterDescription,
|
|
757
782
|
buildFilterTraceLabel: buildFilterTraceLabel,
|
|
783
|
+
buildFilterStageNames: buildFilterStageNames,
|
|
758
784
|
inVivoScoreSourceColumns: inVivoScoreSourceColumns
|
|
759
785
|
}
|