@platforma-open/milaboratories.mixcr-clonotyping-2.workflow 2.0.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.
@@ -0,0 +1,65 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+ self := import("@platforma-sdk/workflow-tengo:tpl")
3
+ pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
4
+ slices := import("@platforma-sdk/workflow-tengo:slices")
5
+ assets := import("@platforma-sdk/workflow-tengo:assets")
6
+ exec := import("@platforma-sdk/workflow-tengo:exec")
7
+
8
+ json := import("json")
9
+
10
+ self.defineOutputs("tsv")
11
+
12
+ ptransformSw := assets.importSoftware("@platforma-open/milaboratories.software-ptransform:main")
13
+
14
+ self.body(func(inputs) {
15
+ inputData := inputs[pConstants.VALUE_FIELD_NAME]
16
+ inputDataMeta := inputData.getDataAsJson()
17
+
18
+ ll.assert(inputDataMeta.keyLength == 1, "unexpected number of aggregation axes")
19
+
20
+ params := inputs.params
21
+ mainAbundanceColumn := params.mainAbundanceColumn
22
+ clonotypeColumns := params.clonotypeColumns
23
+
24
+ // Adding clonotypeKey column
25
+ pWorkflow := {
26
+ steps: [ {
27
+ type: "aggregate",
28
+ groupBy: ["clonotypeKey"],
29
+ aggregations: [ {
30
+ type: "max_by",
31
+ rankingCol: mainAbundanceColumn,
32
+ pickCols: slices.map(clonotypeColumns, func(col) {
33
+ return [col, col]
34
+ })
35
+ } ]
36
+ } ]
37
+ }
38
+
39
+ aggregateBuilderCmd := exec.builder().
40
+ printErrStreamToStdout().
41
+ software(ptransformSw).
42
+ arg("--workflow").arg("wf.json").
43
+ writeFile("wf.json", json.encode(pWorkflow))
44
+
45
+ for sKey, inputFile in inputData.inputs() {
46
+ key := json.decode(sKey)
47
+ if len(key) != 1 {
48
+ ll.panic("malformed key: %v", sKey)
49
+ }
50
+ sampleId := key[0]
51
+ aggregateBuilderCmd.
52
+ arg(sampleId + ".tsv").
53
+ addFile(sampleId + ".tsv", inputFile)
54
+ }
55
+
56
+ aggregateCmd := aggregateBuilderCmd.
57
+ arg("output.tsv").saveFile("output.tsv").
58
+ run()
59
+
60
+ processedTsv := aggregateCmd.getFile("output.tsv")
61
+
62
+ return {
63
+ tsv: processedTsv
64
+ }
65
+ })
@@ -0,0 +1,576 @@
1
+ maps := import("@platforma-sdk/workflow-tengo:maps")
2
+ ll := import("@platforma-sdk/workflow-tengo:ll")
3
+ text := import("text")
4
+ json := import("json")
5
+
6
+ a := func(order, defaultVisibility, spec) {
7
+ return maps.merge(spec, {
8
+ "pl7.app/table/orderPriority": string(order),
9
+ "pl7.app/table/visibility": defaultVisibility ? "default" : "optional"
10
+ })
11
+ }
12
+
13
+ toCombinedDomainValue := func(spec) {
14
+ result := [spec.name]
15
+ // getKeys sort keys
16
+ for domain in maps.getKeys(spec.domain) {
17
+ result = append(result, [domain, spec.domain[domain]])
18
+ }
19
+ return result
20
+ }
21
+
22
+ /**
23
+ * Converts an assembling feature into corresponding productive feature and core features formats.
24
+ * This function handles different immunological feature types (CDR3, VDJRegion, etc.) and
25
+ * returns properly formatted feature expressions based on the input.
26
+ *
27
+ * @param assemblingFeature - The input feature identifier (e.g., "CDR3", "VDJRegion", "FR1_TO_FR4")
28
+ * @return An object containing:
29
+ * - productiveFeature: formatted identifier for the productive feature
30
+ * - coreGeneFeatures: an object containing core features used for mutation analysis:
31
+ * - V: formatted identifier for the V gene core feature
32
+ * - J: formatted identifier for the J gene core feature (if applicable)
33
+ */
34
+ assemblingFeatureInfo := func(assemblingFeature) {
35
+ productiveFeature := undefined
36
+ coreVFeature := undefined
37
+ coreJFeature := undefined
38
+ if assemblingFeature == "CDR3" || is_undefined(assemblingFeature) {
39
+ productiveFeature = "CDR3"
40
+ } else if assemblingFeature == "VDJRegion" {
41
+ productiveFeature = "VDJRegion(0,-1)"
42
+ coreVFeature = "{FR1Begin:FR3End}"
43
+ coreJFeature = "FR4"
44
+ } else {
45
+ splittedFeature := text.split(assemblingFeature, "_TO_")
46
+ if len(splittedFeature) == 2 {
47
+ if splittedFeature[1] == "FR4" {
48
+ productiveFeature = "{"+splittedFeature[0] + "Begin:FR4End(-1)}"
49
+ coreVFeature = "{"+splittedFeature[0]+"Begin:FR3End}"
50
+ coreJFeature = "FR4"
51
+ } else {
52
+ productiveFeature = assemblingFeature
53
+ coreVFeature = "{"+splittedFeature[0]+"Begin:FR3End}"
54
+ }
55
+ }
56
+ }
57
+ return {
58
+ productiveFeature: productiveFeature,
59
+ coreGeneFeatures: {
60
+ V: coreVFeature,
61
+ J: coreJFeature
62
+ }
63
+ }
64
+ }
65
+
66
+ exportSpecOpsFromPreset := func(presetSpecForBack) {
67
+ assemblingFeature := undefined
68
+ if !is_undefined(presetSpecForBack.assemblingFeature) {
69
+ ll.assert(len(presetSpecForBack.assemblingFeature) == 1, "Don't support disjoint assembling features")
70
+ assemblingFeature = presetSpecForBack.assemblingFeature[0]
71
+ }
72
+
73
+ return {
74
+ assemblingFeature: assemblingFeature,
75
+ splitByC: presetSpecForBack.splitByC,
76
+ hasUmi: !is_undefined(presetSpecForBack.umiTags) && len(presetSpecForBack.umiTags) > 0,
77
+ cellTags: presetSpecForBack.cellTags
78
+ }
79
+ }
80
+
81
+ calculateExportSpecs := func(presetSpecForBack, blockId) {
82
+ ops := exportSpecOpsFromPreset(presetSpecForBack)
83
+
84
+ assemblingFeature := ops.assemblingFeature
85
+ cellTags := ops.cellTags
86
+ hasUmi := ops.hasUmi
87
+ splitByC := ops.splitByC
88
+
89
+ assemblingFeatureInfo := assemblingFeatureInfo(assemblingFeature)
90
+ productiveFeature := assemblingFeatureInfo.productiveFeature
91
+ coreGeneFeatures := assemblingFeatureInfo.coreGeneFeatures
92
+
93
+ clonotypeKeyColumns := undefined
94
+ if !is_undefined(assemblingFeature) && (is_undefined(cellTags) || len(cellTags) == 0) {
95
+ clonotypeKeyColumns = ["nSeq" + assemblingFeature, "bestVGene", "bestJGene"]
96
+ if splitByC {
97
+ clonotypeKeyColumns += ["bestCGene"]
98
+ }
99
+ }
100
+
101
+ columnsSpecPerSample := []
102
+ columnsSpecPerClonotype := []
103
+
104
+ // array of array of arg groups
105
+ exportArgs := []
106
+
107
+ // Abundance
108
+
109
+ columnsSpecPerSample += [ {
110
+ column: "readCount",
111
+ id: "read-count",
112
+ allowNA: false,
113
+ spec: {
114
+ name: "pl7.app/vdj/readCount",
115
+ valueType: "Long",
116
+ annotations: a(90000, !hasUmi, {
117
+ "pl7.app/min": "1",
118
+ "pl7.app/isAbundance": "true",
119
+ "pl7.app/abundance/unit": "reads",
120
+ "pl7.app/abundance/normalized": "false",
121
+ "pl7.app/label": "Number Of Reads"
122
+ })
123
+ }
124
+ }, {
125
+ column: "readFraction",
126
+ id: "read-fraction",
127
+ allowNA: false,
128
+ spec: {
129
+ name: "pl7.app/vdj/readFraction",
130
+ valueType: "Double",
131
+ annotations: a(89000, !hasUmi, {
132
+ "pl7.app/min": "0",
133
+ "pl7.app/max": "1",
134
+ "pl7.app/isAbundance": "true",
135
+ "pl7.app/abundance/unit": "reads",
136
+ "pl7.app/abundance/normalized": "true",
137
+ "pl7.app/label": "Fraction of reads"
138
+ })
139
+ }
140
+ } ]
141
+ exportArgs += [
142
+ [ "-readCount" ],
143
+ [ "-readFraction" ]
144
+ ]
145
+ mainAbundanceColumn := "readFraction"
146
+
147
+ if hasUmi {
148
+ columnsSpecPerSample += [ {
149
+ column: "uniqueMoleculeCount",
150
+ id: "umi-count",
151
+ allowNA: false,
152
+ spec: {
153
+ name: "pl7.app/vdj/uniqueMoleculeCount",
154
+ valueType: "Long",
155
+ annotations: a(88000, true, {
156
+ "pl7.app/min": "1",
157
+ "pl7.app/isAbundance": "true",
158
+ "pl7.app/abundance/unit": "molecules",
159
+ "pl7.app/abundance/normalized": "false",
160
+ "pl7.app/label": "Number of UMI"
161
+ })
162
+ }
163
+ }, {
164
+ column: "uniqueMoleculeFraction",
165
+ id: "umi-fraction",
166
+ allowNA: false,
167
+ spec: {
168
+ name: "pl7.app/vdj/uniqueMoleculeFraction",
169
+ valueType: "Double",
170
+ annotations: a(87500, true, {
171
+ "pl7.app/min": "0",
172
+ "pl7.app/max": "1",
173
+ "pl7.app/isAbundance": "true",
174
+ "pl7.app/abundance/unit": "molecules",
175
+ "pl7.app/abundance/normalized": "true",
176
+ "pl7.app/label": "Fraction of UMI"
177
+ })
178
+ }
179
+ } ]
180
+ exportArgs += [
181
+ [ "-uniqueTagCount", "Molecule" ],
182
+ [ "-uniqueTagFraction", "Molecule" ]
183
+ ]
184
+ mainAbundanceColumn = "uniqueMoleculeFraction"
185
+ }
186
+
187
+ // VDJC Hits
188
+
189
+ orderP := 80000
190
+ geneHitColumnVariants := [ {
191
+ name: "pl7.app/vdj/geneHitWithAllele",
192
+ columnNameSuffix: "Hit",
193
+ idSuffix: "-hit-with-allele",
194
+ labelSuffix: " hit with allele",
195
+ argSuffix: "Hit",
196
+ visible: false
197
+ }, {
198
+ name: "pl7.app/vdj/geneHit",
199
+ columnNameSuffix: "Gene",
200
+ idSuffix: "-gene",
201
+ labelSuffix: " gene",
202
+ argSuffix: "Gene",
203
+ visible: true
204
+ } ]
205
+ for vdjcU in ["V", "D", "J", "C"] {
206
+ vdjcL := text.to_lower(vdjcU)
207
+ for variant in geneHitColumnVariants {
208
+ columnsSpecPerClonotype += [ {
209
+ column: "best" + vdjcU + variant.columnNameSuffix,
210
+ id: "best-" + vdjcL + variant.idSuffix,
211
+ naRegex: "",
212
+ allowNA: vdjcU == "C" || vdjcU == "D",
213
+ spec: {
214
+ name: variant.name,
215
+ valueType: "String",
216
+ domain: {
217
+ "pl7.app/vdj/reference": vdjcU + "Gene"
218
+ },
219
+ annotations: a(orderP, variant.visible, {
220
+ "pl7.app/label": "Best " + vdjcU + variant.labelSuffix,
221
+ "pl7.app/isDiscreteFilter": "true"
222
+ })
223
+ }
224
+ } ]
225
+ exportArgs += [ [ "-" + vdjcL + variant.argSuffix ] ]
226
+ orderP -= 100
227
+ }
228
+ }
229
+
230
+ // Sequences
231
+
232
+ features := undefined
233
+ if is_undefined(assemblingFeature) {
234
+ features = ["CDR1", "FR1", "FR2", "CDR2", "FR3", "CDR3", "FR4"]
235
+ } else if assemblingFeature != "CDR3" {
236
+ features = [assemblingFeature, "CDR1", "FR1", "FR2", "CDR2", "FR3", "CDR3", "FR4"]
237
+ } else {
238
+ features = ["CDR3"]
239
+ }
240
+
241
+ for isImputed in ( is_undefined(assemblingFeature) ? [false, true] : [false] ) {
242
+ imputedU := isImputed ? "Imputed" : ""
243
+ imputedL := text.to_lower(imputedU)
244
+ for featureU in features {
245
+ featureL := text.to_lower(featureU)
246
+ for isAminoAcid in [false, true] {
247
+ alphabet := isAminoAcid ? "aminoacid" : "nucleotide"
248
+ alphabetShort := isAminoAcid ? "aa" : "nt"
249
+ alphabetShortMixcr := isAminoAcid ? "aa" : "n"
250
+ visibility := (featureU == "CDR3") || (featureU == assemblingFeature)
251
+ columnsSpecPerClonotype += [ {
252
+ column: alphabetShortMixcr + "Seq" + imputedU + featureU,
253
+ id: alphabetShortMixcr + "-seq-" + featureL + (isImputed ? "-imputed" : ""),
254
+ naRegex: "region_not_covered",
255
+ spec: {
256
+ name: "pl7.app/vdj/sequence",
257
+ valueType: "String",
258
+ domain: {
259
+ "pl7.app/vdj/feature": featureU,
260
+ "pl7.app/alphabet": alphabet
261
+ },
262
+ annotations: a(orderP, visibility, {
263
+ "pl7.app/vdj/imputed": string(isImputed),
264
+ "pl7.app/label": featureU + " " + alphabetShort
265
+ })
266
+ }
267
+ } ]
268
+ exportArgs += [ [ "-" + alphabetShortMixcr + "Feature" + imputedU, featureU ] ]
269
+ if !isImputed && !isAminoAcid {
270
+ columnsSpecPerSample += [ {
271
+ column: "minQual" + featureU,
272
+ id: "min-qual-" + featureL,
273
+ naRegex: "region_not_covered",
274
+ spec: {
275
+ name: "pl7.app/vdj/sequenceQuality",
276
+ valueType: "Int",
277
+ domain: {
278
+ "pl7.app/vdj/quality": "minQuality",
279
+ "pl7.app/vdj/feature": featureU
280
+ },
281
+ annotations: a(orderP - 10, false, {
282
+ "pl7.app/min": "0",
283
+ "pl7.app/max": "60",
284
+ "pl7.app/label": "Min quality " + featureU
285
+ })
286
+ }
287
+ } ]
288
+ exportArgs += [ [ "-minFeatureQuality", featureU ] ]
289
+ }
290
+ orderP -= 100
291
+ }
292
+ }
293
+ }
294
+
295
+ // Mutations
296
+
297
+ orderP = 10000
298
+
299
+ mutationColumnVariants := [ {
300
+ name: "Mutations",
301
+ valueType: "String",
302
+ labelPart: " mutations in ",
303
+ idPart: "-mutations-"
304
+ }, {
305
+ name: "MutationsCount",
306
+ valueType: "Int",
307
+ labelPart: " mutations count in ",
308
+ idPart: "-mutations-count-"
309
+ }, {
310
+ name: "MutationsRate",
311
+ valueType: "Double",
312
+ labelPart: " mutations rate in ",
313
+ idPart: "-mutations-rate-"
314
+ } ]
315
+
316
+ for isAminoAcid in [false, true] {
317
+ alphabetShort := isAminoAcid ? "AA" : "Nt"
318
+ alphabetShortMixcr := isAminoAcid ? "aa" : "n"
319
+
320
+ // Now loop over gene types.
321
+ for geneU in ["V", "J"] {
322
+ geneL := text.to_lower(geneU)
323
+
324
+ coreFeature := coreGeneFeatures[geneU]
325
+ if is_undefined(coreFeature) {
326
+ continue
327
+ }
328
+
329
+ for variant in mutationColumnVariants {
330
+ columnsSpecPerClonotype += [ {
331
+ column: alphabetShortMixcr + variant.name + coreFeature,
332
+ id: alphabetShortMixcr + variant.idPart + geneL,
333
+ allowNA: true,
334
+ naRegex: "region_not_covered",
335
+ spec: {
336
+ valueType: variant.valueType,
337
+ name: "pl7.app/vdj/sequence/" + alphabetShortMixcr + variant.name,
338
+ annotations: a(orderP, false, {
339
+ "pl7.app/label": alphabetShort + variant.labelPart + geneU + " gene"
340
+ })
341
+ }
342
+ } ]
343
+ exportArgs += [ [ "-" + alphabetShortMixcr + variant.name, coreFeature ] ]
344
+ orderP -= 100
345
+ }
346
+ }
347
+ }
348
+
349
+ // Flags: productive, oof, stop codons
350
+
351
+ flagColumnVariants := [ {
352
+ columnPrefix: "isProductive",
353
+ arg: "-isProductive",
354
+ specName: "pl7.app/vdj/sequence/productive",
355
+ label: "Productive",
356
+ id: "is-productive",
357
+ visibility: true
358
+ }, {
359
+ columnPrefix: "isOOF",
360
+ arg: "-isOOF",
361
+ specName: "pl7.app/vdj/sequence/containsOOF",
362
+ label: "Contains OOF",
363
+ id: "is-oof",
364
+ visibility: false
365
+ }, {
366
+ columnPrefix: "hasStopsIn",
367
+ arg: "-hasStops",
368
+ specName: "pl7.app/vdj/sequence/containsStopCodons",
369
+ label: "Contains stop codons",
370
+ id: "has-stops",
371
+ visibility: false
372
+ } ]
373
+ for variant in flagColumnVariants {
374
+ columnsSpecPerClonotype += [ {
375
+ column: variant.columnPrefix + productiveFeature,
376
+ id: variant.id,
377
+ allowNA: false,
378
+ spec: {
379
+ valueType: "String",
380
+ name: variant.specName,
381
+ annotations: a(orderP, variant.visibility, {
382
+ "pl7.app/label": variant.label,
383
+ "pl7.app/isDiscreteFilter": "true",
384
+ "pl7.app/discreteValues": "['true','false']" } )
385
+ }
386
+ } ]
387
+ exportArgs += [ [ variant.arg, productiveFeature ] ]
388
+ orderP -= 100
389
+ }
390
+
391
+ // Germline sequences
392
+
393
+ geneRegions := ["VRegion", "DRegion", "JRegion"]
394
+
395
+ for region in geneRegions {
396
+ columnsSpecPerClonotype += [ {
397
+ column: "nSeq" + region + "OfGermline",
398
+ naRegex: "",
399
+ allowNA: true,
400
+ id: "n-seq-" + text.to_lower(region) + "-germline",
401
+ spec: {
402
+ name: "pl7.app/vdj/germlineSequence",
403
+ valueType: "String",
404
+ domain: {
405
+ "pl7.app/vdj/feature": region,
406
+ "pl7.app/alphabet": "nucleotide"
407
+ },
408
+ annotations: a(orderP, false, {
409
+ "pl7.app/label": region[0:1] + " germline"
410
+ })
411
+ }
412
+ } ]
413
+ exportArgs += [ [ "-nFeature", region, "germline" ] ]
414
+ orderP -= 100
415
+ }
416
+
417
+ // Isotype and chain
418
+
419
+ columnsSpecPerClonotype += [ {
420
+ column: "isotypePrimary",
421
+ id: "isotype",
422
+ naRegex: "",
423
+ spec: {
424
+ valueType: "String",
425
+ name: "pl7.app/vdj/isotype",
426
+ annotations: a(orderP, true, {
427
+ "pl7.app/label": "IG isotype",
428
+ "pl7.app/isDiscreteFilter": "true"
429
+ })
430
+ }
431
+ }, {
432
+ column: "topChains",
433
+ id: "top-chains",
434
+ naRegex: "",
435
+ allowNA: false,
436
+ spec: {
437
+ valueType: "String",
438
+ name: "pl7.app/vdj/chain",
439
+ annotations: a(orderP, true, {
440
+ "pl7.app/label": "Chain",
441
+ "pl7.app/isDiscreteFilter": "true",
442
+ "pl7.app/discreteValues": "['TRA','TRB','TRG','TRD','IGH','IGK','IGL']"
443
+ })
444
+ }
445
+ } ]
446
+ exportArgs += [
447
+ [ "-isotype", "primary" ],
448
+ [ "-topChains" ]
449
+ ]
450
+
451
+ // All columns are added
452
+
453
+ columnsSpec := columnsSpecPerSample + columnsSpecPerClonotype
454
+
455
+ // Creating a column map for fast search
456
+ columnsByName := {}
457
+ for columnSpec in columnsSpec {
458
+ columnsByName[columnSpec.column] = columnSpec
459
+ }
460
+
461
+ // Axes
462
+
463
+ axesByClonotypeKey := undefined
464
+
465
+ if !is_undefined(clonotypeKeyColumns) {
466
+ ll.assert(is_undefined(cellTags) || len(cellTags) == 0, "cellTags and clonotypeKeyAxes cannot both be defined")
467
+
468
+ // checking that corresponding columns exist in export
469
+ keyStrincture := []
470
+ for keyColumn in clonotypeKeyColumns {
471
+ columnSpec := columnsByName[keyColumn]
472
+ if is_undefined(columnSpec) {
473
+ ll.panic("column " + keyColumn + " does not exist in export")
474
+ }
475
+ keyStrincture += [ toCombinedDomainValue(columnSpec.spec) ]
476
+ }
477
+
478
+ axesByClonotypeKey = [ {
479
+ column: "clonotypeKey",
480
+ naRegex: "",
481
+ spec: {
482
+ name: "pl7.app/vdj/clonotypeKey",
483
+ type: "String",
484
+ domain: {
485
+ "pl7.app/vdj/clonotypeKey/structure": string(json.encode(keyStrincture))
486
+ },
487
+ annotations: {
488
+ "pl7.app/label": "Clonotype key",
489
+ "pl7.app/table/visibility": "optional",
490
+ "pl7.app/table/orderPriority": "110000"
491
+ }
492
+ }
493
+ } ]
494
+ }
495
+
496
+ axesByClonotypeId := [ {
497
+ column: "cloneId",
498
+ spec: {
499
+ name: "pl7.app/vdj/cloneId",
500
+ type: "Long",
501
+ domain: {
502
+ "pl7.app/blockId": blockId
503
+ },
504
+ annotations: {
505
+ "pl7.app/min": "0",
506
+ "pl7.app/label": "Clone id",
507
+ "pl7.app/table/visibility": "optional",
508
+ "pl7.app/table/orderPriority": "90000"
509
+ }
510
+ }
511
+ } ]
512
+ exportArgs += [ [ "-cloneId" ] ]
513
+
514
+ orderP = 100000
515
+ if !is_undefined(cellTags) && len(cellTags) > 0 {
516
+ for tag in cellTags {
517
+ label := undefined
518
+ if tag == "CELL" {
519
+ label = "Cell tag"
520
+ } else {
521
+ label = text.to_title(tag[:4]) + " " + text.to_lower(tag[4:])
522
+ }
523
+ axesByClonotypeId += [ {
524
+ column: "tagValue" + tag,
525
+ naRegex: "",
526
+ spec: {
527
+ name: "pl7.app/vdj/cellTag",
528
+ type: "String",
529
+ domain: {
530
+ "pl7.app/vdj/cellTagId": tag,
531
+ "pl7.app/blockId": blockId
532
+ },
533
+ annotations: a(orderP, true, {
534
+ "pl7.app/label": label
535
+ })
536
+ }
537
+ } ]
538
+ orderP -= 1
539
+ }
540
+ exportArgs += [ [ "-tags", "Cell" ] ]
541
+
542
+ columnsSpec += [ {
543
+ column: "cellGroup",
544
+ id: "cell-group",
545
+ naRegex: "undefined|contamination",
546
+ allowNA: true,
547
+ spec: {
548
+ name: "pl7.app/vdj/cellGroup",
549
+ valueType: "Long",
550
+ annotations: a(24000, true, {
551
+ "pl7.app/min": "0",
552
+ "pl7.app/label": "Cell group number"
553
+ } )
554
+ }
555
+ } ]
556
+ exportArgs += [ [ "-cellGroup" ] ]
557
+ }
558
+
559
+ return {
560
+ clonotypeKeyColumns: clonotypeKeyColumns,
561
+
562
+ axesByClonotypeId: axesByClonotypeId,
563
+ axesByClonotypeKey: axesByClonotypeKey,
564
+
565
+ columnsSpecPerSample: columnsSpecPerSample,
566
+ columnsSpecPerClonotype: columnsSpecPerClonotype,
567
+
568
+ columnsSpec: columnsSpec,
569
+
570
+ mainAbundanceColumn: mainAbundanceColumn,
571
+
572
+ exportArgs: exportArgs
573
+ }
574
+ }
575
+
576
+ export calculateExportSpecs
@@ -0,0 +1,74 @@
1
+ // get preset
2
+
3
+ self := import("@platforma-sdk/workflow-tengo:tpl")
4
+ smart := import("@platforma-sdk/workflow-tengo:smart")
5
+ ll := import("@platforma-sdk/workflow-tengo:ll")
6
+ exec := import("@platforma-sdk/workflow-tengo:exec")
7
+ assets := import("@platforma-sdk/workflow-tengo:assets")
8
+
9
+ self.validateInputs({
10
+ "__options__,closed": "",
11
+ preset: "any",
12
+ params: {
13
+ "__options__,closed": "",
14
+ "species,omitempty": "string"
15
+ }
16
+ })
17
+
18
+ mixcrSw := assets.importSoftware("@platforma-open/milaboratories.software-mixcr:low-memory")
19
+
20
+ self.defineOutputs("preset", "presetSpecForBack")
21
+
22
+ self.body(func(inputs) {
23
+ preset := inputs.preset
24
+ species := inputs.params.species
25
+
26
+ mixcrExportPresetCmdBuilder := exec.builder().
27
+ inUiQueue().
28
+ software(mixcrSw).
29
+ secret("MI_LICENSE", "MI_LICENSE").
30
+ printErrStreamToStdout().
31
+ arg("exportPreset")
32
+
33
+ if smart.isResource(preset) /* file */ {
34
+ mixcrExportPresetCmdBuilder.
35
+ arg("--preset-name").
36
+ arg("local#input_preset").
37
+ addFile("input_preset.yaml", preset)
38
+ } else {
39
+ ll.assert(!is_undefined(preset.name), "undefined preset name")
40
+ mixcrExportPresetCmdBuilder.
41
+ arg("--preset-name").
42
+ arg(preset.name)
43
+ }
44
+
45
+ if !is_undefined(species) {
46
+ mixcrExportPresetCmdBuilder.arg("--species").arg(species)
47
+ }
48
+
49
+ mixcrExportPresetCmd := mixcrExportPresetCmdBuilder.arg("preset.json").
50
+ saveFileContent("preset.json").
51
+ saveFile("preset.json").
52
+ run()
53
+
54
+ presetContent := mixcrExportPresetCmd.getFileContent("preset.json")
55
+
56
+ mixcrForBackCmd := exec.builder().
57
+ inUiQueue().
58
+ software(mixcrSw).
59
+ secret("MI_LICENSE", "MI_LICENSE").
60
+ printErrStreamToStdout().
61
+ arg("presetSpecificationsForBack").
62
+ addFile("preset.yaml", mixcrExportPresetCmd.getFile("preset.json")).
63
+ arg("preset.yaml").
64
+ arg("presetForBack.json").
65
+ saveFileContent("presetForBack.json").
66
+ run()
67
+
68
+ presetSpecForBackContent := mixcrForBackCmd.getFileContent("presetForBack.json")
69
+
70
+ return {
71
+ preset: presetContent,
72
+ presetSpecForBack: presetSpecForBackContent
73
+ }
74
+ })