@platforma-open/milaboratories.top-antibodies.workflow 1.17.6 → 2.0.1

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.
@@ -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@1.17.6 build /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow
3
+ > @platforma-open/milaboratories.top-antibodies.workflow@2.0.1 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"...
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @platforma-open/milaboratories.top-antibodies.workflow
2
2
 
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 140ce30: Support custom block label
8
+
9
+ ## 2.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - 590699a: Introduce diverisified ranking, in-vivo score estimation and workflow presets
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [590699a]
18
+ - @platforma-open/milaboratories.top-antibodies.sample-clonotypes@2.0.0
19
+
3
20
  ## 1.17.6
4
21
 
5
22
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  ll := import("@platforma-sdk/workflow-tengo:ll")
2
2
 
3
- getColumns := func(datasetSpec, addRanking) {
3
+ getColumns := func(datasetSpec, addRanking, addInVivoScore) {
4
4
  columns := [{
5
5
  column: "top",
6
6
  id: "link",
@@ -32,7 +32,21 @@ getColumns := func(datasetSpec, addRanking) {
32
32
  }
33
33
  }]
34
34
  }
35
-
35
+ if addInVivoScore {
36
+ columns = columns + [{
37
+ column: "inVivoScore",
38
+ spec: {
39
+ name: "pl7.app/vdj/inVivoScore",
40
+ valueType: "Float",
41
+ domain: {},
42
+ annotations: {
43
+ "pl7.app/label": "In Vivo Score",
44
+ "pl7.app/table/visibility": "optional",
45
+ "pl7.app/format": ".2f"
46
+ }
47
+ }
48
+ }]
49
+ }
36
50
 
37
51
  return {
38
52
  axes: [
@@ -1,16 +1,16 @@
1
1
  ll := import("@platforma-sdk/workflow-tengo:ll")
2
2
  json := import("json")
3
3
 
4
- getColumns := func(datasetSpec, filterMap, rankingMap, disableClusterRanking, clusterColumn, topClonotypes) {
4
+ getColumns := func(datasetSpec, filterMap, rankingMap, diversificationColumn, topClonotypes) {
5
5
 
6
6
 
7
7
  domain := {
8
8
  "pl7.app/vdj/filter-map": string(json.encode(filterMap)),
9
- "pl7.app/vdj/ranking-map": string(json.encode(rankingMap)),
9
+ "pl7.app/vdj/ranking-map": string(json.encode(rankingMap)),
10
10
  "pl7.app/vdj/top-clonotypes": string(topClonotypes)
11
11
  }
12
- if !disableClusterRanking && clusterColumn != undefined && clusterColumn != "" {
13
- domain["pl7.app/vdj/diversity-column"] = string(json.encode(clusterColumn))
12
+ if diversificationColumn != undefined && diversificationColumn != "" {
13
+ domain["pl7.app/vdj/diversity-column"] = string(json.encode(diversificationColumn))
14
14
  }
15
15
 
16
16
  columns := [{
@@ -5,6 +5,13 @@ json := import("json")
5
5
 
6
6
 
7
7
 
8
+ inVivoScoreSourceColumns := {
9
+ "fractionCDR": "pl7.app/vdj/sequence/fractionCDRMutations",
10
+ "nMutations": "pl7.app/vdj/sequence/nMutations"
11
+ }
12
+
13
+
14
+
8
15
 
9
16
 
10
17
 
@@ -175,12 +182,12 @@ buildSortedLinkers := func(columns, datasetSpec) {
175
182
 
176
183
 
177
184
  resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
178
- if is_undefined(args.clusterColumn) {
185
+ if is_undefined(args.diversificationColumn) {
179
186
  return undefined
180
187
  }
181
-
182
188
 
183
- selectedLinkerSpec := columns.getSpec(args.clusterColumn)
189
+
190
+ selectedLinkerSpec := columns.getSpec(args.diversificationColumn)
184
191
  if is_undefined(selectedLinkerSpec) {
185
192
  return undefined
186
193
  }
@@ -273,6 +280,21 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
273
280
  if len(args.rankingOrder) > 0 {
274
281
  for i, col in args.rankingOrder {
275
282
 
283
+ if col.value != undefined && col.value.column == "pl7.app/vdj/inVivoScore" {
284
+
285
+ cloneTable.setAxisHeader(datasetSpec.axesSpec[0], "sampleId")
286
+ cloneTable.add(columns.getColumn(args.inputAnchor), {header: "inVivo_primaryAbundance"})
287
+ addedCols = true
288
+ for key, _ in inVivoScoreSourceColumns {
289
+ for srcCol in columns.getColumns("inVivo_" + key) {
290
+ cloneTable.add(srcCol, {header: "inVivo_" + key})
291
+ addedCols = true
292
+ }
293
+ }
294
+ rankingMap["inVivoScore"] = col.rankingOrder
295
+ continue
296
+ }
297
+
276
298
  if col.value != undefined && columns.getColumn(col.value.column).spec != undefined {
277
299
 
278
300
  colsSpec := columns.getSpec(col.value.column)
@@ -641,5 +663,6 @@ export {
641
663
  detectBulkChain: detectBulkChain,
642
664
  initializeAssemSeqTable: initializeAssemSeqTable,
643
665
  formatFilterDescription: formatFilterDescription,
644
- buildFilterTraceLabel: buildFilterTraceLabel
666
+ buildFilterTraceLabel: buildFilterTraceLabel,
667
+ inVivoScoreSourceColumns: inVivoScoreSourceColumns
645
668
  }
Binary file
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@platforma-open/milaboratories.top-antibodies.workflow",
3
- "version": "1.17.6",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "description": "Block Workflow",
6
6
  "dependencies": {
7
7
  "@platforma-sdk/workflow-tengo": "5.8.2",
8
8
  "@platforma-open/milaboratories.software-anarci": "^0.0.3",
9
- "@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "1.9.3",
10
- "@platforma-open/milaboratories.top-antibodies.spectratype": "1.8.1",
9
+ "@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "2.0.0",
11
10
  "@platforma-open/milaboratories.top-antibodies.assembling-fasta": "1.3.0",
11
+ "@platforma-open/milaboratories.top-antibodies.umap": "1.2.1",
12
12
  "@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.3.0",
13
- "@platforma-open/milaboratories.top-antibodies.umap": "1.2.1"
13
+ "@platforma-open/milaboratories.top-antibodies.spectratype": "1.8.1"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@platforma-sdk/tengo-builder": "2.4.18"
@@ -40,8 +40,16 @@ self.body(func(inputs) {
40
40
  // Save filtered parquet file
41
41
  filteredClonotypes := filterResult.getFile("filteredClonotypes.parquet")
42
42
 
43
- // Store outputs
44
- sampledColsParams := sampledColsConv.getColumns(datasetSpec, false) // No ranking column
43
+ // Check if In Vivo Score is in the ranking map
44
+ hasInVivoScore := false
45
+ for key, _ in rankingMap {
46
+ if key == "inVivoScore" {
47
+ hasInVivoScore = true
48
+ }
49
+ }
50
+
51
+ // Store outputs
52
+ sampledColsParams := sampledColsConv.getColumns(datasetSpec, false, false) // No ranking column
45
53
  filteredClonotypesPf := xsv.importFile(filteredClonotypes, "parquet", sampledColsParams,
46
54
  {cpu: 1, mem: "16GiB"})
47
55
 
@@ -62,12 +70,9 @@ self.body(func(inputs) {
62
70
  arg("--n").arg(string(topClonotypes)).
63
71
  arg("--ranking-map").arg(string(json.encode(rankingMap)))
64
72
 
65
- // Add optional parameters if provided in block args
66
- if inputs.disableClusterRanking {
67
- builder = builder.arg("--disable-cluster-ranking")
68
- }
69
- if inputs.clusterColumn != undefined && inputs.clusterColumn != "" {
70
- builder = builder.arg("--cluster-column").arg(inputs.clusterColumn)
73
+ // Add diversification column if provided
74
+ if inputs.diversificationColumn != undefined && inputs.diversificationColumn != "" {
75
+ builder = builder.arg("--diversification-column").arg(inputs.diversificationColumn)
71
76
  }
72
77
 
73
78
  sampleClones := builder.
@@ -80,8 +85,8 @@ self.body(func(inputs) {
80
85
  // Save top clonotypes parquet file
81
86
  finalClonotypes = sampleClones.getFile("sampledClonotypes_top.parquet")
82
87
 
83
- // Store outputs
84
- sampledColsParams := sampledColsConv.getColumns(datasetSpec, true) // Add ranking column
88
+ // Store outputs
89
+ sampledColsParams := sampledColsConv.getColumns(datasetSpec, true, hasInVivoScore) // Add ranking column
85
90
  sampledColumnsPf := xsv.importFile(finalClonotypes, "parquet", sampledColsParams,
86
91
  {cpu: 1, mem: "16GiB"})
87
92
  outputs["sampledRows"] = pframes.exportFrame(sampledColumnsPf)
@@ -91,7 +96,7 @@ self.body(func(inputs) {
91
96
 
92
97
  // Export only the sampling column to be used as a filter downstream
93
98
  sampledExportsParams := sampledExportConv.getColumns(datasetSpec, filterMap,
94
- rankingMap, inputs.disableClusterRanking, inputs.rawClusterColumn, topClonotypes)
99
+ rankingMap, inputs.diversificationColumn, topClonotypes)
95
100
  sampledColumnsOnlyPf := xsv.importFile(finalClonotypes, "parquet", sampledExportsParams,
96
101
  {cpu: 1, mem: "16GiB"})
97
102
 
@@ -100,6 +105,10 @@ self.body(func(inputs) {
100
105
  if is_undefined(traceLabel) || traceLabel == "" {
101
106
  traceLabel = "Selected Leads"
102
107
  }
108
+ customBlockLabel := inputs.customBlockLabel
109
+ if !is_undefined(customBlockLabel) && customBlockLabel != "" {
110
+ traceLabel = customBlockLabel + " / " + traceLabel
111
+ }
103
112
  trace := pSpec.makeTrace(datasetSpec,
104
113
  {
105
114
  type: "milaboratories.antibody-tcr-lead-selection",
@@ -32,6 +32,19 @@ wf.prepare(func(args){
32
32
  for col in args.rankingOrder {
33
33
  // For cases where the user is selecting the table to filter
34
34
  if col.value != undefined {
35
+ // In Vivo Score is computed from source columns, not a real PColumn
36
+ if col.value.column == "pl7.app/vdj/inVivoScore" {
37
+ // Primary abundance is the input anchor itself — already added above.
38
+ // Fetch the remaining source columns by name.
39
+ for key, name in utils.inVivoScoreSourceColumns {
40
+ bundleBuilder.addMulti({
41
+ axes: [{ anchor: "main", idx: 1 }],
42
+ name: name
43
+ }, "inVivo_" + key)
44
+ }
45
+ validRanks = true
46
+ continue
47
+ }
35
48
  bundleBuilder.addAnchor(col.value.anchorName, col.value.anchorRef)
36
49
  bundleBuilder.addSingle(col.value.column)
37
50
  validRanks = true
@@ -57,9 +70,9 @@ wf.prepare(func(args){
57
70
  partialAxesMatch: true
58
71
  }, "linkers")
59
72
 
60
- // Add clusterColumn as a named anchor for matching in body phase
61
- if !is_undefined(args.clusterColumn) {
62
- bundleBuilder.addAnchor("selectedCluster", args.clusterColumn)
73
+ // Add diversificationColumn as a named anchor for matching in body phase
74
+ if !is_undefined(args.diversificationColumn) {
75
+ bundleBuilder.addAnchor("selectedCluster", args.diversificationColumn)
63
76
  }
64
77
 
65
78
  // Add cluster size columns from clustering blocks
@@ -150,10 +163,9 @@ wf.body(func(args) {
150
163
  rankingMap: rankingMap,
151
164
  datasetSpec: datasetSpec,
152
165
  topClonotypes: args.topClonotypes,
153
- disableClusterRanking: args.disableClusterRanking,
154
- clusterColumn: clusterColumnHeader,
155
- rawClusterColumn: args.clusterColumn,
156
- filterTraceLabel: filterTraceLabel
166
+ diversificationColumn: clusterColumnHeader,
167
+ filterTraceLabel: filterTraceLabel,
168
+ customBlockLabel: args.customBlockLabel
157
169
  })
158
170
 
159
171
  // Get the filtered clonotypes from the template result
@@ -1,6 +1,6 @@
1
1
  ll := import("@platforma-sdk/workflow-tengo:ll")
2
2
 
3
- getColumns := func(datasetSpec, addRanking) {
3
+ getColumns := func(datasetSpec, addRanking, addInVivoScore) {
4
4
  columns := [{
5
5
  column: "top",
6
6
  id: "link",
@@ -32,7 +32,21 @@ getColumns := func(datasetSpec, addRanking) {
32
32
  }
33
33
  }]
34
34
  }
35
-
35
+ if addInVivoScore {
36
+ columns = columns + [{
37
+ column: "inVivoScore",
38
+ spec: {
39
+ name: "pl7.app/vdj/inVivoScore",
40
+ valueType: "Float",
41
+ domain: {},
42
+ annotations: {
43
+ "pl7.app/label": "In Vivo Score",
44
+ "pl7.app/table/visibility": "optional",
45
+ "pl7.app/format": ".2f"
46
+ }
47
+ }
48
+ }]
49
+ }
36
50
 
37
51
  return {
38
52
  axes: [
@@ -1,16 +1,16 @@
1
1
  ll := import("@platforma-sdk/workflow-tengo:ll")
2
2
  json := import("json")
3
3
 
4
- getColumns := func(datasetSpec, filterMap, rankingMap, disableClusterRanking, clusterColumn, topClonotypes) {
4
+ getColumns := func(datasetSpec, filterMap, rankingMap, diversificationColumn, topClonotypes) {
5
5
 
6
6
  // Define domain
7
7
  domain := {
8
8
  "pl7.app/vdj/filter-map": string(json.encode(filterMap)),
9
- "pl7.app/vdj/ranking-map": string(json.encode(rankingMap)),
9
+ "pl7.app/vdj/ranking-map": string(json.encode(rankingMap)),
10
10
  "pl7.app/vdj/top-clonotypes": string(topClonotypes)
11
11
  }
12
- if !disableClusterRanking && clusterColumn != undefined && clusterColumn != "" {
13
- domain["pl7.app/vdj/diversity-column"] = string(json.encode(clusterColumn))
12
+ if diversificationColumn != undefined && diversificationColumn != "" {
13
+ domain["pl7.app/vdj/diversity-column"] = string(json.encode(diversificationColumn))
14
14
  }
15
15
 
16
16
  columns := [{
@@ -3,6 +3,13 @@
3
3
  slices := import("@platforma-sdk/workflow-tengo:slices")
4
4
  json := import("json")
5
5
 
6
+ // PColumn names used as source columns for In Vivo Score computation.
7
+ // Primary abundance is resolved separately via specific annotations.
8
+ inVivoScoreSourceColumns := {
9
+ "fractionCDR": "pl7.app/vdj/sequence/fractionCDRMutations",
10
+ "nMutations": "pl7.app/vdj/sequence/nMutations"
11
+ }
12
+
6
13
  /**
7
14
  * Checks if two clusterId axes have matching domains.
8
15
  * Used to determine if two columns belong to the same clustering run.
@@ -169,18 +176,18 @@ buildSortedLinkers := func(columns, datasetSpec) {
169
176
  /**
170
177
  * Resolves cluster column reference to header name by matching against sortedLinkers.
171
178
  *
172
- * @param args - Arguments containing clusterColumn
179
+ * @param args - Arguments containing diversificationColumn
173
180
  * @param columns - PBundle containing all columns
174
181
  * @param sortedLinkers - List of linker columns in proper order
175
182
  * @return Cluster column header string or undefined
176
183
  */
177
184
  resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
178
- if is_undefined(args.clusterColumn) {
185
+ if is_undefined(args.diversificationColumn) {
179
186
  return undefined
180
187
  }
181
-
182
- // Get the spec for the selected cluster column
183
- selectedLinkerSpec := columns.getSpec(args.clusterColumn)
188
+
189
+ // Get the spec for the selected diversification column
190
+ selectedLinkerSpec := columns.getSpec(args.diversificationColumn)
184
191
  if is_undefined(selectedLinkerSpec) {
185
192
  return undefined
186
193
  }
@@ -220,7 +227,7 @@ resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
220
227
  *
221
228
  * @param pframes - PFrames import
222
229
  * @param columns - PBundle containing all columns
223
- * @param args - Arguments containing filters, rankingOrder, clusterColumn
230
+ * @param args - Arguments containing filters, rankingOrder, diversificationColumn
224
231
  * @param datasetSpec - Dataset specification with axes
225
232
  * @return Map with keys: cloneTable, filterMap, rankingMap, sortedLinkers, clusterColumnHeader, addedCols
226
233
  */
@@ -272,6 +279,21 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
272
279
 
273
280
  if len(args.rankingOrder) > 0 {
274
281
  for i, col in args.rankingOrder {
282
+ // In Vivo Score: add source columns to the clone table for later computation
283
+ if col.value != undefined && col.value.column == "pl7.app/vdj/inVivoScore" {
284
+ // Primary abundance is the input anchor itself; it introduces the sampleId axis
285
+ cloneTable.setAxisHeader(datasetSpec.axesSpec[0], "sampleId")
286
+ cloneTable.add(columns.getColumn(args.inputAnchor), {header: "inVivo_primaryAbundance"})
287
+ addedCols = true
288
+ for key, _ in inVivoScoreSourceColumns {
289
+ for srcCol in columns.getColumns("inVivo_" + key) {
290
+ cloneTable.add(srcCol, {header: "inVivo_" + key})
291
+ addedCols = true
292
+ }
293
+ }
294
+ rankingMap["inVivoScore"] = col.rankingOrder
295
+ continue
296
+ }
275
297
  // we check for value presence and for actual pcolumn (cases where upstream block is deleted)
276
298
  if col.value != undefined && columns.getColumn(col.value.column).spec != undefined {
277
299
  // Process the ranking column to determine header and cluster axis
@@ -401,7 +423,7 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
401
423
  cloneTable.cpu(1)
402
424
  builtTable = cloneTable.build()
403
425
 
404
- // Resolve clusterColumn ref to header name
426
+ // Resolve diversificationColumn ref to header name
405
427
  clusterColumnHeader = resolveClusterColumnHeader(args, columns, sortedLinkers)
406
428
  }
407
429
 
@@ -641,5 +663,6 @@ export {
641
663
  detectBulkChain: detectBulkChain,
642
664
  initializeAssemSeqTable: initializeAssemSeqTable,
643
665
  formatFilterDescription: formatFilterDescription,
644
- buildFilterTraceLabel: buildFilterTraceLabel
666
+ buildFilterTraceLabel: buildFilterTraceLabel,
667
+ inVivoScoreSourceColumns: inVivoScoreSourceColumns
645
668
  }