@platforma-open/milaboratories.top-antibodies.workflow 1.16.0 → 1.17.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.16.0 build /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow
3
+ > @platforma-open/milaboratories.top-antibodies.workflow@1.17.1 build /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow
4
4
  > 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,27 @@
1
1
  # @platforma-open/milaboratories.top-antibodies.workflow
2
2
 
3
+ ## 1.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0b57c1b: Show only specific columns be default: Clone, Cluster Id, AA sequence and filter/rank columns
8
+ - Updated dependencies [0b57c1b]
9
+ - @platforma-open/milaboratories.top-antibodies.sample-clonotypes@1.9.1
10
+
11
+ ## 1.17.0
12
+
13
+ ### Minor Changes
14
+
15
+ - b201aaf: Improve cluster ranking, improve performance
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [b201aaf]
20
+ - @platforma-open/milaboratories.top-antibodies.anarci-kabat@1.3.0
21
+ - @platforma-open/milaboratories.top-antibodies.assembling-fasta@1.3.0
22
+ - @platforma-open/milaboratories.top-antibodies.sample-clonotypes@1.9.0
23
+ - @platforma-open/milaboratories.top-antibodies.spectratype@1.8.0
24
+
3
25
  ## 1.16.0
4
26
 
5
27
  ### Minor Changes
@@ -100,6 +100,7 @@ processRankingColumn := func(colsSpec, datasetMainAxisName, linkerColumns, clust
100
100
 
101
101
  return {
102
102
  isClusterProperty: false,
103
+ isLinkerColumn: false,
103
104
  header: undefined,
104
105
  clusterAxisIdx: undefined,
105
106
  newClusterPropertyIdx: clusterPropertyIdx
@@ -112,11 +113,14 @@ processRankingColumn := func(colsSpec, datasetMainAxisName, linkerColumns, clust
112
113
  header := ""
113
114
  clusterAxisIdx := undefined
114
115
  newClusterPropertyIdx := clusterPropertyIdx
116
+ isLinkerColumn := false
115
117
 
116
118
  if linkerIdx != undefined {
117
119
 
120
+
118
121
  header = "Col_linker." + string(linkerIdx)
119
122
  clusterAxisIdx = linkerIdx
123
+ isLinkerColumn = true
120
124
  } else {
121
125
 
122
126
  header = "Col_cluster." + string(clusterPropertyIdx)
@@ -126,14 +130,430 @@ processRankingColumn := func(colsSpec, datasetMainAxisName, linkerColumns, clust
126
130
 
127
131
  return {
128
132
  isClusterProperty: true,
133
+ isLinkerColumn: isLinkerColumn,
129
134
  header: header,
130
135
  clusterAxisIdx: clusterAxisIdx,
131
136
  newClusterPropertyIdx: newClusterPropertyIdx
132
137
  }
133
138
  }
134
139
 
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+ buildSortedLinkers := func(columns, datasetSpec) {
148
+ allLinkersUnsorted := columns.getColumns("linkers")
149
+
150
+
151
+ sortedLinkers := []
152
+
153
+ for col in allLinkersUnsorted {
154
+ if datasetSpec.axesSpec[1].name == col.spec.axesSpec[1].name {
155
+ sortedLinkers = append(sortedLinkers, col)
156
+ }
157
+ }
158
+
159
+ for col in allLinkersUnsorted {
160
+ if datasetSpec.axesSpec[1].name == col.spec.axesSpec[0].name {
161
+ sortedLinkers = append(sortedLinkers, col)
162
+ }
163
+ }
164
+
165
+ return sortedLinkers
166
+ }
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+ resolveClusterColumnHeader := func(args, columns, sortedLinkers) {
177
+ if is_undefined(args.clusterColumn) {
178
+ return undefined
179
+ }
180
+
181
+
182
+ selectedLinkerSpec := columns.getSpec(args.clusterColumn)
183
+ if is_undefined(selectedLinkerSpec) {
184
+ return undefined
185
+ }
186
+
187
+
188
+ selectedClusterIdAxis := undefined
189
+ for axis in selectedLinkerSpec.axesSpec {
190
+ if axis.name == "pl7.app/vdj/clusterId" {
191
+ selectedClusterIdAxis = axis
192
+ break
193
+ }
194
+ }
195
+
196
+ if is_undefined(selectedClusterIdAxis) {
197
+ return undefined
198
+ }
199
+
200
+
201
+ for linkerIdx, col in sortedLinkers {
202
+
203
+ for axis in col.spec.axesSpec {
204
+ if axis.name == "pl7.app/vdj/clusterId" {
205
+
206
+ if clusterAxisDomainsMatch(selectedClusterIdAxis, axis) {
207
+ return "clusterAxis_" + string(linkerIdx) + "_0"
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ return undefined
214
+ }
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+ initializeCloneTable := func(pframes, columns, args, datasetSpec) {
227
+
228
+ cloneTable := pframes.parquetFileBuilder()
229
+ cloneTable.setAxisHeader(datasetSpec.axesSpec[1], "clonotypeKey")
230
+
231
+
232
+ sortedLinkers := buildSortedLinkers(columns, datasetSpec)
233
+
234
+
235
+ addedAxes := []
236
+ filterMap := {}
237
+ rankingMap := {}
238
+ addedCols := false
239
+
240
+ if len(args.filters) > 0 {
241
+ for i, filter in args.filters {
242
+
243
+ if filter.value != undefined && columns.getColumn(filter.value.column).spec != undefined {
244
+
245
+ cloneTable.add(columns.getColumn(filter.value.column),
246
+ {header: "Filter_" + string(i), id: "filter_" + string(i)})
247
+ addedCols = true
248
+
249
+ filterMap["Filter_" + string(i)] = filter.filter
250
+ filterMap["Filter_" + string(i)]["valueType"] = columns.getSpec(filter.value.column).valueType
251
+
252
+
253
+ colsSpec := columns.getSpec(filter.value.column)
254
+ axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
255
+ if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
256
+ for na, ax in colsSpec.axesSpec {
257
+ if ax.name != datasetSpec.axesSpec[1].name {
258
+ cloneTable.setAxisHeader(ax, "cluster_" + string(i) + string(na))
259
+ addedAxes = append(addedAxes, ax.name)
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+
267
+
268
+ clusterPropertyIdx := 0
269
+ clonotypePropertyIdx := 0
270
+ linkerColumnCounters := {} // Track column count per linker index
271
+
272
+ if len(args.rankingOrder) > 0 {
273
+ for i, col in args.rankingOrder {
274
+
275
+ if col.value != undefined && columns.getColumn(col.value.column).spec != undefined {
276
+
277
+ colsSpec := columns.getSpec(col.value.column)
278
+
279
+ result := processRankingColumn(colsSpec, datasetSpec.axesSpec[1].name, sortedLinkers, clusterPropertyIdx)
280
+
281
+ header := ""
282
+ if result.isClusterProperty {
283
+
284
+ if result.isLinkerColumn {
285
+
286
+ linkerKey := "linker_" + string(result.clusterAxisIdx)
287
+ if is_undefined(linkerColumnCounters[linkerKey]) {
288
+ linkerColumnCounters[linkerKey] = 0
289
+ }
290
+ counter := linkerColumnCounters[linkerKey]
291
+ header = "Col_linker." + string(result.clusterAxisIdx) + "." + string(counter)
292
+ linkerColumnCounters[linkerKey] = counter + 1
293
+ } else {
294
+ header = result.header
295
+ clusterPropertyIdx = result.newClusterPropertyIdx
296
+ }
297
+
298
+
299
+ for na, ax in colsSpec.axesSpec {
300
+ if ax.name != datasetSpec.axesSpec[1].name && !slices.hasElement(addedAxes, ax.name) {
301
+ axisHeader := "cluster_" + string(result.clusterAxisIdx)
302
+ cloneTable.setAxisHeader(ax, axisHeader)
303
+ addedAxes = append(addedAxes, ax.name)
304
+ }
305
+ }
306
+ } else {
307
+ header = "Col" + string(clonotypePropertyIdx)
308
+ clonotypePropertyIdx = clonotypePropertyIdx + 1
309
+ }
310
+
311
+ cloneTable.add(columns.getColumn(col.value.column), {header: header})
312
+ addedCols = true
313
+ rankingMap[header] = col.rankingOrder
314
+ }
315
+ }
316
+ }
317
+
318
+
319
+ linkerClusterIdAxesWithIdx := []
320
+
321
+ for linkerIdx, col in sortedLinkers {
322
+ clusterIdAxis := undefined
323
+ if datasetSpec.axesSpec[1].name == col.spec.axesSpec[1].name {
324
+
325
+ cloneTable.add(col, {header: "linker." + string(linkerIdx)})
326
+ cloneTable.setAxisHeader(col.spec.axesSpec[0], "cluster_" + string(linkerIdx))
327
+ clusterIdAxis = col.spec.axesSpec[0]
328
+ addedCols = true
329
+ } else if datasetSpec.axesSpec[1].name == col.spec.axesSpec[0].name {
330
+
331
+ cloneTable.add(col, {header: "linker." + string(linkerIdx)})
332
+ cloneTable.setAxisHeader(col.spec.axesSpec[1], "cluster_" + string(linkerIdx))
333
+ clusterIdAxis = col.spec.axesSpec[1]
334
+ addedCols = true
335
+ }
336
+
337
+ if !is_undefined(clusterIdAxis) && clusterIdAxis.name == "pl7.app/vdj/clusterId" {
338
+ linkerClusterIdAxesWithIdx = append(linkerClusterIdAxesWithIdx, {
339
+ axis: clusterIdAxis,
340
+ linkerIdx: linkerIdx
341
+ })
342
+ }
343
+ }
344
+
345
+
346
+ if len(columns.getColumns("clusterSizes")) > 0 {
347
+ for col in columns.getColumns("clusterSizes") {
348
+
349
+ clusterSizeClusterIdAxis := undefined
350
+ for axis in col.spec.axesSpec {
351
+ if axis.name == "pl7.app/vdj/clusterId" {
352
+ clusterSizeClusterIdAxis = axis
353
+ break
354
+ }
355
+ }
356
+
357
+
358
+ matchingLinkerIdx := -1
359
+ if len(linkerClusterIdAxesWithIdx) > 0 && !is_undefined(clusterSizeClusterIdAxis) {
360
+ for entry in linkerClusterIdAxesWithIdx {
361
+ linkerAxis := entry.axis
362
+
363
+ if clusterSizeClusterIdAxis.name == linkerAxis.name &&
364
+ clusterSizeClusterIdAxis.type == linkerAxis.type &&
365
+ clusterAxisDomainsMatch(clusterSizeClusterIdAxis, linkerAxis) {
366
+ matchingLinkerIdx = entry.linkerIdx
367
+ break
368
+ }
369
+ }
370
+ }
371
+
372
+
373
+ if matchingLinkerIdx >= 0 {
374
+ cloneTable.add(col, {header: "clusterSize." + string(matchingLinkerIdx)})
375
+ addedCols = true
376
+
377
+ for axisIdx, axis in col.spec.axesSpec {
378
+ if axis.name != datasetSpec.axesSpec[1].name {
379
+ cloneTable.setAxisHeader(axis, "clusterAxis_" + string(matchingLinkerIdx) + "_" + string(axisIdx))
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+
387
+ if !addedCols {
388
+ cdr3Sequences := columns.getColumns("cdr3Sequences")
389
+ if len(cdr3Sequences) > 0 {
390
+ cloneTable.add(cdr3Sequences[0], {header: "cdr3_fallback"})
391
+ addedCols = true
392
+ }
393
+ }
394
+
395
+
396
+ builtTable := undefined
397
+ clusterColumnHeader := undefined
398
+ if addedCols {
399
+ cloneTable.mem("16GiB")
400
+ cloneTable.cpu(1)
401
+ builtTable = cloneTable.build()
402
+
403
+
404
+ clusterColumnHeader = resolveClusterColumnHeader(args, columns, sortedLinkers)
405
+ }
406
+
407
+ return {
408
+ cloneTable: builtTable,
409
+ filterMap: filterMap,
410
+ rankingMap: rankingMap,
411
+ sortedLinkers: sortedLinkers,
412
+ clusterColumnHeader: clusterColumnHeader,
413
+ addedCols: addedCols
414
+ }
415
+ }
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+ makeHeaderName := func(col, baseHeaderName, isSingleCell) {
426
+ chainMapping := {
427
+ "IG": { "A": "Heavy", "B": "Light" },
428
+ "TCRAB": { "A": "TRA", "B": "TRB" },
429
+ "TCRGD": { "A": "TRG", "B": "TRD" }
430
+ }
431
+
432
+ if isSingleCell {
433
+ chain := col.spec.domain["pl7.app/vdj/scClonotypeChain"] // e.g., "A", "B"
434
+ receptor := col.spec.axesSpec[0].domain["pl7.app/vdj/receptor"] // e.g., "IG", "TCRAB", "TCRGD"
435
+ chainLabel := chainMapping[receptor][chain]
436
+ return baseHeaderName + "." + chainLabel // e.g., "cdr3Sequence.Heavy"
437
+ } else {
438
+
439
+ chainFromDomain := col.spec.axesSpec[0].domain["pl7.app/vdj/chain"] // e.g. "IGH", "IGK"
440
+ if chainFromDomain != undefined {
441
+ return baseHeaderName + "." + chainFromDomain // e.g., "cdr3Sequence.IGH"
442
+ }
443
+ }
444
+ return baseHeaderName
445
+ }
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+ initializeCdr3SeqTable := func(pframes, columns, datasetSpec, isSingleCell) {
457
+ cdr3SeqTable := pframes.parquetFileBuilder()
458
+ cdr3SeqTable.setAxisHeader(datasetSpec.axesSpec[1].name, "clonotypeKey")
459
+
460
+
461
+ cdr3Sequences := columns.getColumns("cdr3Sequences")
462
+ for col in cdr3Sequences {
463
+ headerName := makeHeaderName(col, "cdr3Sequence", isSingleCell)
464
+ if isSingleCell {
465
+ if col.spec.domain["pl7.app/vdj/scClonotypeChain/index"] == "primary" {
466
+ cdr3SeqTable.add(col, {header: headerName})
467
+ }
468
+ } else {
469
+ cdr3SeqTable.add(col, {header: headerName})
470
+ }
471
+ }
472
+
473
+
474
+ vGenes := columns.getColumns("VGenes")
475
+ for col in vGenes {
476
+ headerName := makeHeaderName(col, "vGene", isSingleCell)
477
+ cdr3SeqTable.add(col, {header: headerName})
478
+ }
479
+
480
+
481
+ jGenes := columns.getColumns("JGenes")
482
+ for col in jGenes {
483
+ headerName := makeHeaderName(col, "jGene", isSingleCell)
484
+ cdr3SeqTable.add(col, {header: headerName})
485
+ }
486
+
487
+ cdr3SeqTable.mem("16GiB")
488
+ cdr3SeqTable.cpu(1)
489
+ return cdr3SeqTable.build()
490
+ }
491
+
492
+
493
+
494
+
495
+
496
+
497
+
498
+ detectBulkChain := func(seqCols) {
499
+ chainDetected := "KL"
500
+ for col in seqCols {
501
+ ch := col.spec.axesSpec[0].domain["pl7.app/vdj/chain"] // e.g., IGHeavy, IGLight
502
+ if ch == "IGHeavy" {
503
+ chainDetected = "H"
504
+ break
505
+ }
506
+ if ch == "IGLight" {
507
+ chainDetected = "KL"
508
+ }
509
+ }
510
+ return chainDetected
511
+ }
512
+
513
+
514
+
515
+
516
+
517
+
518
+
519
+
520
+
521
+
522
+ initializeAssemSeqTable := func(pframes, columns, datasetSpec, isSingleCell) {
523
+ assemSeqTable := pframes.parquetFileBuilder()
524
+ assemSeqTable.setAxisHeader(datasetSpec.axesSpec[1].name, "clonotypeKey")
525
+
526
+ seqCols := columns.getColumns("assemblingAaSeqs")
527
+ for col in seqCols {
528
+ headerName := makeHeaderName(col, "assemblingFeature", isSingleCell)
529
+ assemSeqTable.add(col, {header: headerName})
530
+ }
531
+
532
+ assemSeqTable.mem("16GiB")
533
+ assemSeqTable.cpu(1)
534
+
535
+
536
+ bulkChain := undefined
537
+ if !isSingleCell {
538
+ bulkChain = detectBulkChain(seqCols)
539
+ }
540
+
541
+ return {
542
+ assemSeqTable: assemSeqTable.build(),
543
+ bulkChain: bulkChain,
544
+ seqCols: seqCols
545
+ }
546
+ }
547
+
135
548
  export {
136
549
  clusterAxisDomainsMatch: clusterAxisDomainsMatch,
137
550
  findMatchingLinkerIndex: findMatchingLinkerIndex,
138
- processRankingColumn: processRankingColumn
551
+ processRankingColumn: processRankingColumn,
552
+ buildSortedLinkers: buildSortedLinkers,
553
+ resolveClusterColumnHeader: resolveClusterColumnHeader,
554
+ initializeCloneTable: initializeCloneTable,
555
+ makeHeaderName: makeHeaderName,
556
+ initializeCdr3SeqTable: initializeCdr3SeqTable,
557
+ detectBulkChain: detectBulkChain,
558
+ initializeAssemSeqTable: initializeAssemSeqTable
139
559
  }
Binary file
package/package.json CHANGED
@@ -1,25 +1,23 @@
1
1
  {
2
2
  "name": "@platforma-open/milaboratories.top-antibodies.workflow",
3
- "version": "1.16.0",
3
+ "version": "1.17.1",
4
4
  "type": "module",
5
5
  "description": "Block Workflow",
6
6
  "dependencies": {
7
- "@platforma-sdk/workflow-tengo": "^5.7.3",
7
+ "@platforma-sdk/workflow-tengo": "5.7.3",
8
8
  "@platforma-open/milaboratories.software-anarci": "^0.0.3",
9
- "@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "1.8.0",
9
+ "@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "1.9.1",
10
+ "@platforma-open/milaboratories.top-antibodies.spectratype": "1.8.0",
10
11
  "@platforma-open/milaboratories.top-antibodies.umap": "1.2.1",
11
- "@platforma-open/milaboratories.top-antibodies.assembling-fasta": "1.2.1",
12
- "@platforma-open/milaboratories.top-antibodies.spectratype": "1.7.1",
13
- "@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.2.1"
12
+ "@platforma-open/milaboratories.top-antibodies.assembling-fasta": "1.3.0",
13
+ "@platforma-open/milaboratories.top-antibodies.anarci-kabat": "1.3.0"
14
14
  },
15
15
  "devDependencies": {
16
- "@platforma-sdk/tengo-builder": "^2.4.2",
17
- "@platforma-sdk/test": "^1.48.8",
18
- "vitest": "^2.1.8"
16
+ "@platforma-sdk/tengo-builder": "2.4.8"
19
17
  },
20
18
  "scripts": {
21
19
  "build": "rm -rf dist && pl-tengo check && pl-tengo build",
22
- "test": "vitest",
23
- "format": "/usr/bin/env emacs --script ./format.el"
20
+ "format": "/usr/bin/env emacs --script ./format.el",
21
+ "do-pack": "rm -f *.tgz && pnpm pack && mv *.tgz package.tgz"
24
22
  }
25
23
  }