@platforma-open/milaboratories.top-antibodies.workflow 3.0.1 → 4.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@3.0.1 build /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow
3
+ > @platforma-open/milaboratories.top-antibodies.workflow@4.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,21 @@
1
1
  # @platforma-open/milaboratories.top-antibodies.workflow
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - dd754ae: Accept both pre- and post-peptide-adaptation spec names from upstream blocks so projects using either version remain functional:
8
+
9
+ - Preset filter/ranking allowlists now include `pl7.app/enrichment*` (clonotype-enrichment) and `pl7.app/developability*` (antibody-sequence-liabilities) alongside the legacy `pl7.app/vdj/`-prefixed names.
10
+ - Diversification dropdown, cluster-axis matching, hidden cluster-mapping column, and workflow-side linker matching now recognize both `pl7.app/clusterId` and `pl7.app/vdj/clusterId` axis names (clonotype-clustering rename).
11
+ - Cluster-size query uses a namePattern matching both `pl7.app/clustering/clusterSize` and `pl7.app/vdj/clustering/clusterSize`.
12
+
13
+ ## 4.0.0
14
+
15
+ ### Major Changes
16
+
17
+ - 1c1c7c1: Support peptides
18
+
3
19
  ## 3.0.1
4
20
 
5
21
  ### Patch Changes
@@ -6,7 +6,7 @@ getColumns := func(datasetSpec, addRanking, addInVivoScore) {
6
6
  id: "link",
7
7
  allowNA: false,
8
8
  spec: {
9
- name: "pl7.app/vdj/lead-selection",
9
+ name: "pl7.app/lead-selection",
10
10
  valueType: "Int",
11
11
  domain: {},
12
12
  annotations: {
@@ -21,7 +21,7 @@ getColumns := func(datasetSpec, addRanking, addInVivoScore) {
21
21
  columns = columns + [{
22
22
  column: "ranked_order",
23
23
  spec: {
24
- name: "pl7.app/vdj/ranking-order",
24
+ name: "pl7.app/ranking-order",
25
25
  valueType: "Int",
26
26
  domain: {},
27
27
  annotations: {
@@ -18,7 +18,7 @@ getColumns := func(datasetSpec, filterMap, rankingMap, diversificationColumn, to
18
18
  id: "link",
19
19
  allowNA: false,
20
20
  spec: {
21
- name: "pl7.app/vdj/lead-selection",
21
+ name: "pl7.app/lead-selection",
22
22
  valueType: "Int",
23
23
  domain: domain,
24
24
  annotations: {
@@ -13,6 +13,13 @@ inVivoScoreSourceColumns := {
13
13
 
14
14
 
15
15
 
16
+ isClusterIdAxis := func(name) {
17
+ return name == "pl7.app/clusterId" || name == "pl7.app/vdj/clusterId"
18
+ }
19
+
20
+
21
+
22
+
16
23
 
17
24
 
18
25
 
@@ -61,7 +68,7 @@ findMatchingLinkerIndex := func(colsSpec, linkerColumns) {
61
68
 
62
69
  rankingClusterIdAxis := undefined
63
70
  for axis in colsSpec.axesSpec {
64
- if axis.name == "pl7.app/vdj/clusterId" {
71
+ if isClusterIdAxis(axis.name) {
65
72
  rankingClusterIdAxis = axis
66
73
  break
67
74
  }
@@ -76,7 +83,7 @@ findMatchingLinkerIndex := func(colsSpec, linkerColumns) {
76
83
 
77
84
  linkerClusterIdAxis := undefined
78
85
  for axis in linkerCol.spec.axesSpec {
79
- if axis.name == "pl7.app/vdj/clusterId" {
86
+ if isClusterIdAxis(axis.name) {
80
87
  linkerClusterIdAxis = axis
81
88
  break
82
89
  }
@@ -195,7 +202,7 @@ resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
195
202
 
196
203
  selectedClusterIdAxis := undefined
197
204
  for axis in selectedLinkerSpec.axesSpec {
198
- if axis.name == "pl7.app/vdj/clusterId" {
205
+ if isClusterIdAxis(axis.name) {
199
206
  selectedClusterIdAxis = axis
200
207
  break
201
208
  }
@@ -209,7 +216,7 @@ resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
209
216
  for linkerIdx, col in sortedLinkers {
210
217
 
211
218
  for axis in col.spec.axesSpec {
212
- if axis.name == "pl7.app/vdj/clusterId" {
219
+ if isClusterIdAxis(axis.name) {
213
220
 
214
221
  if clusterAxisDomainsMatch(selectedClusterIdAxis, axis) {
215
222
  return "clusterAxis_" + string(linkerIdx) + "_0"
@@ -409,7 +416,7 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
409
416
  addedCols = true
410
417
  }
411
418
 
412
- if !is_undefined(clusterIdAxis) && clusterIdAxis.name == "pl7.app/vdj/clusterId" {
419
+ if !is_undefined(clusterIdAxis) && isClusterIdAxis(clusterIdAxis.name) {
413
420
  linkerClusterIdAxesWithIdx = append(linkerClusterIdAxesWithIdx, {
414
421
  axis: clusterIdAxis,
415
422
  linkerIdx: linkerIdx
@@ -423,7 +430,7 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
423
430
 
424
431
  clusterSizeClusterIdAxis := undefined
425
432
  for axis in col.spec.axesSpec {
426
- if axis.name == "pl7.app/vdj/clusterId" {
433
+ if isClusterIdAxis(axis.name) {
427
434
  clusterSizeClusterIdAxis = axis
428
435
  break
429
436
  }
Binary file
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@platforma-open/milaboratories.top-antibodies.workflow",
3
- "version": "3.0.1",
3
+ "version": "4.0.1",
4
4
  "type": "module",
5
5
  "description": "Block Workflow",
6
6
  "dependencies": {
7
- "@platforma-sdk/workflow-tengo": "5.16.0",
7
+ "@platforma-sdk/workflow-tengo": "5.20.0",
8
8
  "@platforma-open/milaboratories.software-anarci": "^0.0.3",
9
- "@platforma-open/milaboratories.top-antibodies.spectratype": "1.8.3",
10
9
  "@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "2.1.2",
11
- "@platforma-open/milaboratories.top-antibodies.umap": "1.2.3",
12
10
  "@platforma-open/milaboratories.top-antibodies.assembling-fasta": "1.3.2",
13
- "@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.4.3"
11
+ "@platforma-open/milaboratories.top-antibodies.umap": "1.2.3",
12
+ "@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.4.3",
13
+ "@platforma-open/milaboratories.top-antibodies.spectratype": "1.8.3"
14
14
  },
15
15
  "devDependencies": {
16
- "@platforma-sdk/tengo-builder": "2.5.17"
16
+ "@platforma-sdk/tengo-builder": "2.5.20"
17
17
  },
18
18
  "scripts": {
19
19
  "build": "shx rm -rf dist && pl-tengo check && pl-tengo build",
@@ -75,9 +75,11 @@ wf.prepare(func(args){
75
75
  bundleBuilder.addAnchor("selectedCluster", args.diversificationColumn)
76
76
  }
77
77
 
78
- // Add cluster size columns from clustering blocks
78
+ // Add cluster size columns from clustering blocks. The namePattern matches
79
+ // both the post-peptide-adaptation name and the pre-peptide `pl7.app/vdj/`
80
+ // variant so older clonotype-clustering instances remain usable.
79
81
  bundleBuilder.addMulti({
80
- name: "pl7.app/vdj/clustering/clusterSize",
82
+ namePattern: "^pl7\\.app/(vdj/)?clustering/clusterSize$",
81
83
  partialAxesMatch: true
82
84
  }, "clusterSizes")
83
85
 
@@ -134,6 +136,7 @@ wf.body(func(args) {
134
136
 
135
137
  // Needed conditional variable
136
138
  isSingleCell := datasetSpec.axesSpec[1].name == "pl7.app/vdj/scClonotypeKey"
139
+ isPeptide := datasetSpec.axesSpec[1].name == "pl7.app/variantKey"
137
140
 
138
141
  ////////// Clonotype Filtering //////////
139
142
  // Initialize and build clone table with all columns
@@ -177,76 +180,79 @@ wf.body(func(args) {
177
180
  // Export only the sampling column for downstream (built inside sub-template to avoid hang)
178
181
  exports["sampledColumnsPf"] = filterSampleResult.output("sampledColumnsExport", 24 * 60 * 60 * 1000)
179
182
 
180
- ////////// CDR3 Length Calculation //////////
181
- // Initialize and build CDR3 sequence table
182
- cdr3SeqTableBuilt := utils.initializeCdr3SeqTable(pframes, columns, datasetSpec, isSingleCell)
183
+ // CDR3 spectratype, V/J gene usage, and Kabat numbering are VDJ-only — skip for peptide inputs.
184
+ if !isPeptide {
185
+ ////////// CDR3 Length Calculation //////////
186
+ // Initialize and build CDR3 sequence table
187
+ cdr3SeqTableBuilt := utils.initializeCdr3SeqTable(pframes, columns, datasetSpec, isSingleCell)
183
188
 
184
- cdr3VspectratypeCmd := exec.builder().
185
- software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.spectratype:main")).
186
- mem("16GiB").
187
- cpu(1).
188
- addFile("cdr3_sequences_input.parquet", cdr3SeqTableBuilt).
189
- arg("--input_parquet").arg("cdr3_sequences_input.parquet").
190
- arg("--spectratype_tsv").arg("spectratype.tsv").
191
- arg("--vj_usage_tsv").arg("vj_usage.tsv") // no dot here
189
+ cdr3VspectratypeCmd := exec.builder().
190
+ software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.spectratype:main")).
191
+ mem("16GiB").
192
+ cpu(1).
193
+ addFile("cdr3_sequences_input.parquet", cdr3SeqTableBuilt).
194
+ arg("--input_parquet").arg("cdr3_sequences_input.parquet").
195
+ arg("--spectratype_tsv").arg("spectratype.tsv").
196
+ arg("--vj_usage_tsv").arg("vj_usage.tsv") // no dot here
192
197
 
193
- // Add top clonotypes argument and file to the builder if provided
194
- if finalClonotypes != undefined {
195
- cdr3VspectratypeCmd = cdr3VspectratypeCmd.
196
- arg("--final-clonotypes").arg("finalClonotypes.parquet").
197
- addFile("finalClonotypes.parquet", finalClonotypes)
198
- }
198
+ // Add top clonotypes argument and file to the builder if provided
199
+ if finalClonotypes != undefined {
200
+ cdr3VspectratypeCmd = cdr3VspectratypeCmd.
201
+ arg("--final-clonotypes").arg("finalClonotypes.parquet").
202
+ addFile("finalClonotypes.parquet", finalClonotypes)
203
+ }
199
204
 
200
- cdr3VspectratypeCmd = cdr3VspectratypeCmd. // continue building the command
201
- saveFile("spectratype.tsv").
202
- saveFile("vj_usage.tsv").
203
- printErrStreamToStdout().
204
- cache(24 * 60 * 60 * 1000).
205
- run()
205
+ cdr3VspectratypeCmd = cdr3VspectratypeCmd. // continue building the command
206
+ saveFile("spectratype.tsv").
207
+ saveFile("vj_usage.tsv").
208
+ printErrStreamToStdout().
209
+ cache(24 * 60 * 60 * 1000).
210
+ run()
206
211
 
207
212
 
208
- // Spectratype PFrame structure is [chain][cdr3Length][vGene] -> count
213
+ // Spectratype PFrame structure is [chain][cdr3Length][vGene] -> count
209
214
 
210
- cdr3VspectratypePf := xsv.importFile(cdr3VspectratypeCmd.getFile("spectratype.tsv"),
211
- "tsv", spectratypeConv.getColumns(),
212
- {cpu: 1, mem: "16GiB"})
213
- outputs["cdr3VspectratypePf"] = pframes.exportFrame(cdr3VspectratypePf)
215
+ cdr3VspectratypePf := xsv.importFile(cdr3VspectratypeCmd.getFile("spectratype.tsv"),
216
+ "tsv", spectratypeConv.getColumns(),
217
+ {cpu: 1, mem: "16GiB"})
218
+ outputs["cdr3VspectratypePf"] = pframes.exportFrame(cdr3VspectratypePf)
214
219
 
215
- // For vjUsage structure is [chain][vGene][jGene] -> count
216
- vjUsagePf := xsv.importFile(cdr3VspectratypeCmd.getFile("vj_usage.tsv"),
217
- "tsv", vjUsageConv.getColumns(),
218
- {cpu: 1, mem: "16GiB"})
219
- outputs["vjUsagePf"] = pframes.exportFrame(vjUsagePf)
220
+ // For vjUsage structure is [chain][vGene][jGene] -> count
221
+ vjUsagePf := xsv.importFile(cdr3VspectratypeCmd.getFile("vj_usage.tsv"),
222
+ "tsv", vjUsageConv.getColumns(),
223
+ {cpu: 1, mem: "16GiB"})
224
+ outputs["vjUsagePf"] = pframes.exportFrame(vjUsagePf)
220
225
 
221
- if args.kabatNumbering == true {
222
- ////////// Assembling AA sequences //////////
223
- // Initialize and build assembling sequence table
224
- assemInit := utils.initializeAssemSeqTable(pframes, columns, datasetSpec, isSingleCell)
225
- assemSeqTableBuilt := assemInit.assemSeqTable
226
- bulkChain := assemInit.bulkChain
227
- seqCols := assemInit.seqCols
226
+ if args.kabatNumbering == true {
227
+ ////////// Assembling AA sequences //////////
228
+ // Initialize and build assembling sequence table
229
+ assemInit := utils.initializeAssemSeqTable(pframes, columns, datasetSpec, isSingleCell)
230
+ assemSeqTableBuilt := assemInit.assemSeqTable
231
+ bulkChain := assemInit.bulkChain
232
+ seqCols := assemInit.seqCols
228
233
 
229
- // Convert assembling feature sequences to FASTA via sub-template
230
- assemFastaTpl := assets.importTemplate(":assembling-fasta")
231
- assem := render.create(assemFastaTpl, {
232
- inputTsv: assemSeqTableBuilt,
233
- keyColumn: "clonotypeKey",
234
- finalClonotypes: finalClonotypes,
235
- isSingleCell: isSingleCell,
236
- bulkChain: bulkChain
237
- })
238
- //outputs["assemblingAnarci"] = assem.output("anarci", 24 * 60 * 60 * 1000)
239
- kabatFile := assem.output("kabat", 24 * 60 * 60 * 1000)
240
- // Derive feature name from assembling feature columns (prefer first column's feature)
241
- featName := ""
242
- if len(seqCols) > 0 {
243
- f := seqCols[0].spec.domain["pl7.app/vdj/feature"]
244
- if f != undefined { featName = f }
234
+ // Convert assembling feature sequences to FASTA via sub-template
235
+ assemFastaTpl := assets.importTemplate(":assembling-fasta")
236
+ assem := render.create(assemFastaTpl, {
237
+ inputTsv: assemSeqTableBuilt,
238
+ keyColumn: "clonotypeKey",
239
+ finalClonotypes: finalClonotypes,
240
+ isSingleCell: isSingleCell,
241
+ bulkChain: bulkChain
242
+ })
243
+ //outputs["assemblingAnarci"] = assem.output("anarci", 24 * 60 * 60 * 1000)
244
+ kabatFile := assem.output("kabat", 24 * 60 * 60 * 1000)
245
+ // Derive feature name from assembling feature columns (prefer first column's feature)
246
+ featName := ""
247
+ if len(seqCols) > 0 {
248
+ f := seqCols[0].spec.domain["pl7.app/vdj/feature"]
249
+ if f != undefined { featName = f }
250
+ }
251
+ // Convert kabat.tsv to PFrame with proper specs (bulk: select heavy/light)
252
+ kabatPf := xsv.importFile(kabatFile, "tsv", kabatConv.getColumns(datasetSpec, featName, bulkChain), {cpu: 1, mem: "8GiB"})
253
+ outputs["assemblingKabatPf"] = pframes.exportFrame(kabatPf)
254
+ outputs["kabatStatsContent"] = assem.output("kabatStats")
245
255
  }
246
- // Convert kabat.tsv to PFrame with proper specs (bulk: select heavy/light)
247
- kabatPf := xsv.importFile(kabatFile, "tsv", kabatConv.getColumns(datasetSpec, featName, bulkChain), {cpu: 1, mem: "8GiB"})
248
- outputs["assemblingKabatPf"] = pframes.exportFrame(kabatPf)
249
- outputs["kabatStatsContent"] = assem.output("kabatStats")
250
256
  }
251
257
  }
252
258
  }
@@ -6,7 +6,7 @@ getColumns := func(datasetSpec, addRanking, addInVivoScore) {
6
6
  id: "link",
7
7
  allowNA: false,
8
8
  spec: {
9
- name: "pl7.app/vdj/lead-selection",
9
+ name: "pl7.app/lead-selection",
10
10
  valueType: "Int",
11
11
  domain: {},
12
12
  annotations: {
@@ -21,7 +21,7 @@ getColumns := func(datasetSpec, addRanking, addInVivoScore) {
21
21
  columns = columns + [{
22
22
  column: "ranked_order",
23
23
  spec: {
24
- name: "pl7.app/vdj/ranking-order",
24
+ name: "pl7.app/ranking-order",
25
25
  valueType: "Int",
26
26
  domain: {},
27
27
  annotations: {
@@ -18,7 +18,7 @@ getColumns := func(datasetSpec, filterMap, rankingMap, diversificationColumn, to
18
18
  id: "link",
19
19
  allowNA: false,
20
20
  spec: {
21
- name: "pl7.app/vdj/lead-selection",
21
+ name: "pl7.app/lead-selection",
22
22
  valueType: "Int",
23
23
  domain: domain,
24
24
  annotations: {
@@ -10,6 +10,13 @@ inVivoScoreSourceColumns := {
10
10
  "nMutations": "pl7.app/vdj/sequence/nMutations"
11
11
  }
12
12
 
13
+ // Cluster-id axis names — both unprefixed (post-peptide-adaptation) and
14
+ // `pl7.app/vdj/`-prefixed (pre-peptide) so older clonotype-clustering
15
+ // instances remain selectable as diversification linkers.
16
+ isClusterIdAxis := func(name) {
17
+ return name == "pl7.app/clusterId" || name == "pl7.app/vdj/clusterId"
18
+ }
19
+
13
20
  /**
14
21
  * Checks if two clusterId axes have matching domains.
15
22
  * Used to determine if two columns belong to the same clustering run.
@@ -61,7 +68,7 @@ findMatchingLinkerIndex := func(colsSpec, linkerColumns) {
61
68
  // Find the clusterId axis in the ranking column
62
69
  rankingClusterIdAxis := undefined
63
70
  for axis in colsSpec.axesSpec {
64
- if axis.name == "pl7.app/vdj/clusterId" {
71
+ if isClusterIdAxis(axis.name) {
65
72
  rankingClusterIdAxis = axis
66
73
  break
67
74
  }
@@ -76,7 +83,7 @@ findMatchingLinkerIndex := func(colsSpec, linkerColumns) {
76
83
  // Get the clusterId axis from the linker column
77
84
  linkerClusterIdAxis := undefined
78
85
  for axis in linkerCol.spec.axesSpec {
79
- if axis.name == "pl7.app/vdj/clusterId" {
86
+ if isClusterIdAxis(axis.name) {
80
87
  linkerClusterIdAxis = axis
81
88
  break
82
89
  }
@@ -195,7 +202,7 @@ resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
195
202
  // Find the clusterId axis in the selected linker
196
203
  selectedClusterIdAxis := undefined
197
204
  for axis in selectedLinkerSpec.axesSpec {
198
- if axis.name == "pl7.app/vdj/clusterId" {
205
+ if isClusterIdAxis(axis.name) {
199
206
  selectedClusterIdAxis = axis
200
207
  break
201
208
  }
@@ -209,7 +216,7 @@ resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
209
216
  for linkerIdx, col in sortedLinkers {
210
217
  // Get the clusterId axis from this linker
211
218
  for axis in col.spec.axesSpec {
212
- if axis.name == "pl7.app/vdj/clusterId" {
219
+ if isClusterIdAxis(axis.name) {
213
220
  // Use clusterAxisDomainsMatch for proper domain comparison
214
221
  if clusterAxisDomainsMatch(selectedClusterIdAxis, axis) {
215
222
  return "clusterAxis_" + string(linkerIdx) + "_0"
@@ -409,7 +416,7 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
409
416
  addedCols = true
410
417
  }
411
418
  // Collect clusterId axes from linker columns to match cluster size columns
412
- if !is_undefined(clusterIdAxis) && clusterIdAxis.name == "pl7.app/vdj/clusterId" {
419
+ if !is_undefined(clusterIdAxis) && isClusterIdAxis(clusterIdAxis.name) {
413
420
  linkerClusterIdAxesWithIdx = append(linkerClusterIdAxesWithIdx, {
414
421
  axis: clusterIdAxis,
415
422
  linkerIdx: linkerIdx
@@ -423,7 +430,7 @@ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
423
430
  // Find the clusterId axis in this cluster size column
424
431
  clusterSizeClusterIdAxis := undefined
425
432
  for axis in col.spec.axesSpec {
426
- if axis.name == "pl7.app/vdj/clusterId" {
433
+ if isClusterIdAxis(axis.name) {
427
434
  clusterSizeClusterIdAxis = axis
428
435
  break
429
436
  }