@platforma-open/milaboratories.mixcr-amplicon-alignment.workflow 1.14.0 → 1.16.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.
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/work/mixcr-amplicon-alignment/mixcr-amplicon-alignment/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-open/milaboratories.mixcr-amplicon-alignment.workflow@1.14.0 build /home/runner/work/mixcr-amplicon-alignment/mixcr-amplicon-alignment/workflow
3
+ > @platforma-open/milaboratories.mixcr-amplicon-alignment.workflow@1.16.0 build /home/runner/work/mixcr-amplicon-alignment/mixcr-amplicon-alignment/workflow
4
4
  > rm -rf dist && pl-tengo check && pl-tengo build
5
5
 
6
6
  Processing "src/aggregate-by-clonotype-key.tpl.tengo"...
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @platforma-open/milaboratories.mixcr-amplicon-alignment.workflow
2
2
 
3
+ ## 1.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 187f583: Fix repseqio fasta generation, dependencies updates
8
+
9
+ ## 1.15.0
10
+
11
+ ### Minor Changes
12
+
13
+ - a08b796: multiple UMI bug fix
14
+
3
15
  ## 1.14.0
4
16
 
5
17
  ### Minor Changes
@@ -3,9 +3,13 @@
3
3
 
4
4
  ll := import("@platforma-sdk/workflow-tengo:ll")
5
5
  pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
6
+ text := import("text")
6
7
 
7
8
 
8
- getQcReportColumns := func(hasUmi, sampleIdAxisSpec, chains) {
9
+ getQcReportColumns := func(hasUmi, sampleIdAxisSpec, chains, umiTags) {
10
+ if is_undefined(umiTags) {
11
+ umiTags = []
12
+ }
9
13
 
10
14
  baseColumns := [
11
15
  {
@@ -622,73 +626,82 @@ getQcReportColumns := func(hasUmi, sampleIdAxisSpec, chains) {
622
626
  bulkOrder -= 100
623
627
  }
624
628
 
625
- dataWithUmiColumns := [ {
626
- column: "refineTags.UMI.outputCount",
627
- id: "refine-tags-umi-output-count",
628
- allowNA: true,
629
- naRegex: "NaN",
630
- spec: {
631
- name: "mixcr.com/reports/refineTags/UMI/outputCount",
632
- valueType: "Long",
633
- annotations: {
634
- "pl7.app/min": "0",
635
- "pl7.app/table/orderPriority": "85000",
636
- "pl7.app/table/visibility": "optional",
637
- "pl7.app/label": "Refine Tags UMI - Output Count"
629
+ dataWithUmiColumns := []
630
+ for idx, umiTag in umiTags {
631
+ orderBase := 85000 + idx * 10
632
+ orderBasePercents := 85100 + idx * 10
633
+ orderDiversity := 85200 + idx * 10
634
+ orderDiversityPercents := 85300 + idx * 10
635
+ tagL := text.to_lower(umiTag)
636
+ dataWithUmiColumns = dataWithUmiColumns + [{
637
+ column: "refineTags." + umiTag + ".outputCount",
638
+ id: "refine-tags-" + tagL + "-output-count",
639
+ allowNA: true,
640
+ naRegex: "NaN",
641
+ spec: {
642
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputCount",
643
+ valueType: "Long",
644
+ annotations: {
645
+ "pl7.app/min": "0",
646
+ "pl7.app/table/orderPriority": string(orderBase),
647
+ "pl7.app/table/visibility": "optional",
648
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Count"
649
+ }
638
650
  }
639
- }
640
- },
641
- {
642
- column: "refineTags.UMI.outputCountPercents",
643
- id: "refine-tags-umi-output-count-percents",
644
- allowNA: true,
645
- naRegex: "NaN",
646
- spec: {
647
- name: "mixcr.com/reports/refineTags/UMI/outputCountPercents",
648
- valueType: "Double",
649
- annotations: {
650
- "pl7.app/min": "0",
651
- "pl7.app/max": "100",
652
- "pl7.app/table/orderPriority": "85100",
653
- "pl7.app/table/visibility": "default",
654
- "pl7.app/label": "Refine Tags UMI - Output Count (%)"
651
+ },
652
+ {
653
+ column: "refineTags." + umiTag + ".outputCountPercents",
654
+ id: "refine-tags-" + tagL + "-output-count-percents",
655
+ allowNA: true,
656
+ naRegex: "NaN",
657
+ spec: {
658
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputCountPercents",
659
+ valueType: "Double",
660
+ annotations: {
661
+ "pl7.app/min": "0",
662
+ "pl7.app/max": "100",
663
+ "pl7.app/table/orderPriority": string(orderBasePercents),
664
+ "pl7.app/table/visibility": "default",
665
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Count (%)"
666
+ }
655
667
  }
656
- }
657
- },
658
- {
659
- column: "refineTags.UMI.outputDiversity",
660
- id: "refine-tags-umi-output-diversity",
661
- allowNA: true,
662
- naRegex: "NaN",
663
- spec: {
664
- name: "mixcr.com/reports/refineTags/UMI/outputDiversity",
665
- valueType: "Long",
666
- annotations: {
667
- "pl7.app/min": "0",
668
- "pl7.app/table/orderPriority": "85200",
669
- "pl7.app/table/visibility": "optional",
670
- "pl7.app/label": "Refine Tags UMI - Output Diversity"
668
+ },
669
+ {
670
+ column: "refineTags." + umiTag + ".outputDiversity",
671
+ id: "refine-tags-" + tagL + "-output-diversity",
672
+ allowNA: true,
673
+ naRegex: "NaN",
674
+ spec: {
675
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputDiversity",
676
+ valueType: "Long",
677
+ annotations: {
678
+ "pl7.app/min": "0",
679
+ "pl7.app/table/orderPriority": string(orderDiversity),
680
+ "pl7.app/table/visibility": "optional",
681
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Diversity"
682
+ }
671
683
  }
672
- }
673
- },
674
- {
675
- column: "refineTags.UMI.outputDiversityPercents",
676
- id: "refine-tags-umi-output-diversity-percents",
677
- allowNA: true,
678
- naRegex: "NaN",
679
- spec: {
680
- name: "mixcr.com/reports/refineTags/UMI/outputDiversityPercents",
681
- valueType: "Double",
682
- annotations: {
683
- "pl7.app/min": "0",
684
- "pl7.app/max": "100",
685
- "pl7.app/table/orderPriority": "85300",
686
- "pl7.app/table/visibility": "default",
687
- "pl7.app/label": "Refine Tags UMI - Output Diversity (%)"
684
+ },
685
+ {
686
+ column: "refineTags." + umiTag + ".outputDiversityPercents",
687
+ id: "refine-tags-" + tagL + "-output-diversity-percents",
688
+ allowNA: true,
689
+ naRegex: "NaN",
690
+ spec: {
691
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputDiversityPercents",
692
+ valueType: "Double",
693
+ annotations: {
694
+ "pl7.app/min": "0",
695
+ "pl7.app/max": "100",
696
+ "pl7.app/table/orderPriority": string(orderDiversityPercents),
697
+ "pl7.app/table/visibility": "default",
698
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Diversity (%)"
699
+ }
688
700
  }
689
- }
690
- },
691
- {
701
+ }]
702
+ }
703
+
704
+ dataWithUmiColumns = dataWithUmiColumns + [{
692
705
  column: "refineTags.numberOfGroupsAccepted",
693
706
  id: "refine-tags-number-of-groups-accepted",
694
707
  allowNA: true,
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@platforma-open/milaboratories.mixcr-amplicon-alignment.workflow",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "MiXCR Amplicon Alignment Workflow",
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@platforma-sdk/workflow-tengo": "^5.7.3",
7
+ "@platforma-sdk/workflow-tengo": "^5.8.0",
8
8
  "@platforma-open/milaboratories.software-mixcr": "4.7.0-254-develop",
9
9
  "@platforma-open/milaboratories.software-repseqio": "^2.5.0-13-master"
10
10
  },
11
11
  "devDependencies": {
12
- "@platforma-sdk/tengo-builder": "^2.4.2",
13
- "@platforma-sdk/test": "^1.48.8",
14
- "vitest": "^2.1.8"
12
+ "@platforma-sdk/tengo-builder": "^2.4.11",
13
+ "@platforma-sdk/test": "^1.52.4",
14
+ "vitest": "^4.0.17"
15
15
  },
16
16
  "scripts": {
17
17
  "build": "rm -rf dist && pl-tengo check && pl-tengo build",
@@ -30,7 +30,8 @@ self.body(func(inputs) {
30
30
  isLibraryFileGzipped := inputs.isLibraryFileGzipped
31
31
  clonotypeTablesData := inputs.clonotypeTablesData
32
32
 
33
- hasUmi := !is_undefined(presetSpecForBack.umiTags) && len(presetSpecForBack.umiTags) > 0
33
+ umiTags := inputs.umiTags
34
+ hasUmi := !is_undefined(umiTags) && len(umiTags) > 0
34
35
 
35
36
  chainInfos := {
36
37
  "IGHeavy": { mixcrFilter: "IGH", name: "IG Heavy", shortName: "Heavy" },
@@ -173,7 +174,7 @@ self.body(func(inputs) {
173
174
 
174
175
  tsvFile := wfResult.getFile("qc-report-processed.tsv")
175
176
 
176
- qcReportColumns := qcReportColumns(hasUmi, sampleIdAxisSpec, chains)
177
+ qcReportColumns := qcReportColumns(hasUmi, sampleIdAxisSpec, chains, umiTags)
177
178
  reportColumnsSpec := qcReportColumns.reportColumnsSpec
178
179
 
179
180
  qcReportTable := xsv.importFile(
@@ -32,27 +32,6 @@ wf.body(func(args) {
32
32
  perProcessCPUs := args.perProcessCPUs
33
33
  cloneClusteringMode := args.cloneClusteringMode
34
34
 
35
- // Create V and J gene FASTA files
36
-
37
- fastaFileCmdBuilder := exec.builder().cmd("/usr/bin/env").arg("bash")
38
-
39
- // Save vGene to FASTA file if available
40
- if args.vGenes != "" {
41
- fastaFileCmdBuilder.writeFile("vGene.fasta", args.vGenes).
42
- saveFile("vGene.fasta")
43
- }
44
-
45
- // Save jGene to FASTA file if available
46
- if args.jGenes != "" {
47
- fastaFileCmdBuilder.writeFile("jGene.fasta", args.jGenes).
48
- saveFile("jGene.fasta")
49
- }
50
-
51
- fastaFileCmd := fastaFileCmdBuilder.run()
52
-
53
- vGeneFasta := fastaFileCmd.getFile("vGene.fasta")
54
- jGeneFasta := fastaFileCmd.getFile("jGene.fasta")
55
-
56
35
  chainInfos := {
57
36
  "IGHeavy": { mixcrFilter: "IGH", name: "IG Heavy", shortName: "Heavy" },
58
37
  "IGLight": { mixcrFilter: "IGK", name: "IG Light", shortName: "Light" },
@@ -65,8 +44,8 @@ wf.body(func(args) {
65
44
  // Generate reference library using repseqio if both V and J genes are available
66
45
 
67
46
  repseqioResults := render.create(repseqioLibraryTpl, {
68
- vGeneFasta: vGeneFasta,
69
- jGeneFasta: jGeneFasta,
47
+ vGenes: args.vGenes,
48
+ jGenes: args.jGenes,
70
49
  chains: chainInfos[chains].mixcrFilter
71
50
  })
72
51
 
@@ -34,7 +34,39 @@ self.body(func(inputs) {
34
34
  cloneClusteringMode := params.cloneClusteringMode
35
35
  tagPattern := params.tagPattern
36
36
 
37
- hasUMI := text.contains(tagPattern, "UMI") || text.contains(tagPattern, "Umi") || text.contains(tagPattern, "umi")
37
+ extractUmiTags := func(pattern) {
38
+ if is_undefined(pattern) || pattern == "" {
39
+ return []
40
+ }
41
+ umiTags := []
42
+ parts := text.split(pattern, "(")
43
+ for idx, part in parts {
44
+ if idx == 0 {
45
+ continue
46
+ }
47
+ if !text.contains(part, ":") {
48
+ continue
49
+ }
50
+ name := text.split(part, ":")[0]
51
+ if !text.has_prefix(text.to_lower(name), "umi") {
52
+ continue
53
+ }
54
+ exists := false
55
+ for tag in umiTags {
56
+ if tag == name {
57
+ exists = true
58
+ break
59
+ }
60
+ }
61
+ if !exists {
62
+ umiTags = append(umiTags, name)
63
+ }
64
+ }
65
+ return umiTags
66
+ }
67
+
68
+ umiTags := extractUmiTags(tagPattern)
69
+ hasUMI := len(umiTags) > 0
38
70
  limitInput := inputs.limitInput
39
71
  perProcessMemGB := params.perProcessMemGB
40
72
  perProcessCPUs := params.perProcessCPUs
@@ -45,7 +77,7 @@ self.body(func(inputs) {
45
77
  presetSpecForBack := {
46
78
  assemblingFeature: params.assemblingFeature,
47
79
  splitByC: true,
48
- umiTags: hasUMI ? ["Molecule"] : undefined,
80
+ umiTags: hasUMI ? umiTags : undefined,
49
81
  cellTags: []
50
82
  }
51
83
 
@@ -291,12 +323,13 @@ self.body(func(inputs) {
291
323
 
292
324
  qcReportTable := render.create(exportReportTpl, {
293
325
  clnsData: mixcrResults.outputData("clns"),
294
- presetSpecForBack: presetSpecForBack,
295
326
  sampleIdAxisSpec: sampleIdAxisSpec,
296
327
  chains: [chains],
297
328
  library: referenceLibrary,
298
329
  isLibraryFileGzipped: false,
299
- clonotypeTablesData: clonotypeTablesData
330
+ clonotypeTablesData: clonotypeTablesData,
331
+ hasUmi: hasUMI,
332
+ umiTags: umiTags
300
333
  })
301
334
 
302
335
  return {
@@ -3,9 +3,13 @@
3
3
 
4
4
  ll := import("@platforma-sdk/workflow-tengo:ll")
5
5
  pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
6
+ text := import("text")
6
7
 
7
8
  // QC Report column specifications function
8
- getQcReportColumns := func(hasUmi, sampleIdAxisSpec, chains) {
9
+ getQcReportColumns := func(hasUmi, sampleIdAxisSpec, chains, umiTags) {
10
+ if is_undefined(umiTags) {
11
+ umiTags = []
12
+ }
9
13
  // Bulk sequencing columns
10
14
  baseColumns := [
11
15
  {
@@ -622,73 +626,82 @@ getQcReportColumns := func(hasUmi, sampleIdAxisSpec, chains) {
622
626
  bulkOrder -= 100
623
627
  }
624
628
 
625
- dataWithUmiColumns := [ {
626
- column: "refineTags.UMI.outputCount",
627
- id: "refine-tags-umi-output-count",
628
- allowNA: true,
629
- naRegex: "NaN",
630
- spec: {
631
- name: "mixcr.com/reports/refineTags/UMI/outputCount",
632
- valueType: "Long",
633
- annotations: {
634
- "pl7.app/min": "0",
635
- "pl7.app/table/orderPriority": "85000",
636
- "pl7.app/table/visibility": "optional",
637
- "pl7.app/label": "Refine Tags UMI - Output Count"
629
+ dataWithUmiColumns := []
630
+ for idx, umiTag in umiTags {
631
+ orderBase := 85000 + idx * 10
632
+ orderBasePercents := 85100 + idx * 10
633
+ orderDiversity := 85200 + idx * 10
634
+ orderDiversityPercents := 85300 + idx * 10
635
+ tagL := text.to_lower(umiTag)
636
+ dataWithUmiColumns = dataWithUmiColumns + [{
637
+ column: "refineTags." + umiTag + ".outputCount",
638
+ id: "refine-tags-" + tagL + "-output-count",
639
+ allowNA: true,
640
+ naRegex: "NaN",
641
+ spec: {
642
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputCount",
643
+ valueType: "Long",
644
+ annotations: {
645
+ "pl7.app/min": "0",
646
+ "pl7.app/table/orderPriority": string(orderBase),
647
+ "pl7.app/table/visibility": "optional",
648
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Count"
649
+ }
638
650
  }
639
- }
640
- },
641
- {
642
- column: "refineTags.UMI.outputCountPercents",
643
- id: "refine-tags-umi-output-count-percents",
644
- allowNA: true,
645
- naRegex: "NaN",
646
- spec: {
647
- name: "mixcr.com/reports/refineTags/UMI/outputCountPercents",
648
- valueType: "Double",
649
- annotations: {
650
- "pl7.app/min": "0",
651
- "pl7.app/max": "100",
652
- "pl7.app/table/orderPriority": "85100",
653
- "pl7.app/table/visibility": "default",
654
- "pl7.app/label": "Refine Tags UMI - Output Count (%)"
651
+ },
652
+ {
653
+ column: "refineTags." + umiTag + ".outputCountPercents",
654
+ id: "refine-tags-" + tagL + "-output-count-percents",
655
+ allowNA: true,
656
+ naRegex: "NaN",
657
+ spec: {
658
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputCountPercents",
659
+ valueType: "Double",
660
+ annotations: {
661
+ "pl7.app/min": "0",
662
+ "pl7.app/max": "100",
663
+ "pl7.app/table/orderPriority": string(orderBasePercents),
664
+ "pl7.app/table/visibility": "default",
665
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Count (%)"
666
+ }
655
667
  }
656
- }
657
- },
658
- {
659
- column: "refineTags.UMI.outputDiversity",
660
- id: "refine-tags-umi-output-diversity",
661
- allowNA: true,
662
- naRegex: "NaN",
663
- spec: {
664
- name: "mixcr.com/reports/refineTags/UMI/outputDiversity",
665
- valueType: "Long",
666
- annotations: {
667
- "pl7.app/min": "0",
668
- "pl7.app/table/orderPriority": "85200",
669
- "pl7.app/table/visibility": "optional",
670
- "pl7.app/label": "Refine Tags UMI - Output Diversity"
668
+ },
669
+ {
670
+ column: "refineTags." + umiTag + ".outputDiversity",
671
+ id: "refine-tags-" + tagL + "-output-diversity",
672
+ allowNA: true,
673
+ naRegex: "NaN",
674
+ spec: {
675
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputDiversity",
676
+ valueType: "Long",
677
+ annotations: {
678
+ "pl7.app/min": "0",
679
+ "pl7.app/table/orderPriority": string(orderDiversity),
680
+ "pl7.app/table/visibility": "optional",
681
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Diversity"
682
+ }
671
683
  }
672
- }
673
- },
674
- {
675
- column: "refineTags.UMI.outputDiversityPercents",
676
- id: "refine-tags-umi-output-diversity-percents",
677
- allowNA: true,
678
- naRegex: "NaN",
679
- spec: {
680
- name: "mixcr.com/reports/refineTags/UMI/outputDiversityPercents",
681
- valueType: "Double",
682
- annotations: {
683
- "pl7.app/min": "0",
684
- "pl7.app/max": "100",
685
- "pl7.app/table/orderPriority": "85300",
686
- "pl7.app/table/visibility": "default",
687
- "pl7.app/label": "Refine Tags UMI - Output Diversity (%)"
684
+ },
685
+ {
686
+ column: "refineTags." + umiTag + ".outputDiversityPercents",
687
+ id: "refine-tags-" + tagL + "-output-diversity-percents",
688
+ allowNA: true,
689
+ naRegex: "NaN",
690
+ spec: {
691
+ name: "mixcr.com/reports/refineTags/" + umiTag + "/outputDiversityPercents",
692
+ valueType: "Double",
693
+ annotations: {
694
+ "pl7.app/min": "0",
695
+ "pl7.app/max": "100",
696
+ "pl7.app/table/orderPriority": string(orderDiversityPercents),
697
+ "pl7.app/table/visibility": "default",
698
+ "pl7.app/label": "Refine Tags " + umiTag + " - Output Diversity (%)"
699
+ }
688
700
  }
689
- }
690
- },
691
- {
701
+ }]
702
+ }
703
+
704
+ dataWithUmiColumns = dataWithUmiColumns + [{
692
705
  column: "refineTags.numberOfGroupsAccepted",
693
706
  id: "refine-tags-number-of-groups-accepted",
694
707
  allowNA: true,
@@ -12,9 +12,6 @@ repseqioSw := assets.importSoftware("@platforma-open/milaboratories.software-rep
12
12
  self.defineOutputs("referenceLibrary", "debugOutput")
13
13
 
14
14
  self.body(func(inputs) {
15
- // Get V and J gene FASTA files from prerun
16
- vGeneFasta := inputs.vGeneFasta
17
- jGeneFasta := inputs.jGeneFasta
18
15
 
19
16
  // Create repseqio command to generate reference library
20
17
  repseqioVgeneCmd := exec.builder().
@@ -26,7 +23,8 @@ self.body(func(inputs) {
26
23
  arg("--taxon-id").arg("1111").
27
24
  arg("--gene-feature").arg("VRegion").
28
25
  arg("--name-index").arg("0").
29
- arg("vGene.fasta").addFile("vGene.fasta", vGeneFasta).
26
+ writeFile("vGene.fasta", inputs.vGenes).saveFile("vGene.fasta").
27
+ arg("vGene.fasta").
30
28
  arg("vGene.json").saveFile("vGene.json").
31
29
  cpu(1).mem("4GB")
32
30
 
@@ -35,6 +33,7 @@ self.body(func(inputs) {
35
33
 
36
34
  // Get the generated reference library file
37
35
  referenceLibraryVgene := repseqioVgeneResult.getFile("vGene.json")
36
+ vGeneFasta := repseqioVgeneResult.getFile("vGene.fasta")
38
37
 
39
38
  repseqioJgeneCmd := exec.builder().
40
39
  software(repseqioSw).
@@ -45,13 +44,14 @@ self.body(func(inputs) {
45
44
  arg("--taxon-id").arg("1111").
46
45
  arg("--gene-feature").arg("JRegion").
47
46
  arg("--name-index").arg("0").
48
- arg("jGene.fasta").addFile("jGene.fasta", jGeneFasta).
47
+ writeFile("jGene.fasta", inputs.jGenes).saveFile("jGene.fasta").
48
+ arg("jGene.fasta").
49
49
  arg("jGene.json").saveFile("jGene.json").
50
50
  cpu(1).mem("4GB")
51
51
 
52
52
  repseqioJgeneResult := repseqioJgeneCmd.run()
53
53
  referenceLibraryJgene := repseqioJgeneResult.getFile("jGene.json")
54
-
54
+ jGeneFasta := repseqioJgeneResult.getFile("jGene.fasta")
55
55
  repseqioMergeCmd := exec.builder().
56
56
  software(repseqioSw).
57
57
  arg("merge").