@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,26 @@
1
+ // list presets
2
+
3
+ self := import("@platforma-sdk/workflow-tengo:tpl")
4
+ ll := import("@platforma-sdk/workflow-tengo:ll")
5
+ exec := import("@platforma-sdk/workflow-tengo:exec")
6
+ file := import("@platforma-sdk/workflow-tengo:file")
7
+ assets := import("@platforma-sdk/workflow-tengo:assets")
8
+
9
+ self.defineOutputs("presets")
10
+
11
+ mixcrSw := assets.importSoftware("@platforma-open/milaboratories.software-mixcr:low-memory")
12
+
13
+ self.body(func(inputs) {
14
+ listPresets := exec.builder().
15
+ inUiQueue().
16
+ software(mixcrSw).
17
+ secret("MI_LICENSE", "MI_LICENSE").
18
+ arg("listPresetSpecificationsForUI").
19
+ arg("presets.json").
20
+ saveFile("presets.json").
21
+ run()
22
+
23
+ return {
24
+ presets: file.exportFile(listPresets.getFile("presets.json"))
25
+ }
26
+ })
@@ -0,0 +1,159 @@
1
+ wf := import("@platforma-sdk/workflow-tengo:workflow")
2
+
3
+ render := import("@platforma-sdk/workflow-tengo:render")
4
+
5
+ maps := import("@platforma-sdk/workflow-tengo:maps")
6
+ ll := import("@platforma-sdk/workflow-tengo:ll")
7
+ assets := import("@platforma-sdk/workflow-tengo:assets")
8
+ smart := import("@platforma-sdk/workflow-tengo:smart")
9
+ file := import("@platforma-sdk/workflow-tengo:file")
10
+ llPFrames := import("@platforma-sdk/workflow-tengo:pframes.ll")
11
+ pframes := import("@platforma-sdk/workflow-tengo:pframes")
12
+ exec := import("@platforma-sdk/workflow-tengo:exec")
13
+ self := import("@platforma-sdk/workflow-tengo:tpl")
14
+
15
+ json := import("json")
16
+
17
+ calculatePresetInfoTpl := assets.importTemplate(":calculate-preset-info")
18
+ processTpl := assets.importTemplate(":process")
19
+
20
+ wf.setPreRun(assets.importTemplate(":prerun"))
21
+
22
+ wf.prepare(func(args){
23
+ return{
24
+ resolvedLibraryInput: wf.resolve(args.inputLibrary, { errIfMissing: false })
25
+ }
26
+ })
27
+
28
+ wf.body(func(args) {
29
+ // (begin) @TODO To be moved to SDK / Model
30
+
31
+ fileImports := {}
32
+
33
+ importFile := func(importHandle) {
34
+ fromMap := fileImports[importHandle]
35
+ if fromMap == undefined {
36
+ fImport := file.importFile(importHandle)
37
+ fileImports[importHandle] = fImport
38
+ return fImport.file
39
+ } else {
40
+ return fromMap.file
41
+ }
42
+ }
43
+
44
+ // (end)
45
+
46
+ blockId := wf.blockId().getDataAsJson()
47
+
48
+ inputRef := args.input
49
+
50
+ preset := args.preset
51
+
52
+ // file or json with preset name
53
+ presetResource := undefined
54
+
55
+ if preset.type == "file" {
56
+ presetResource = importFile(preset.file)
57
+ } else {
58
+ ll.assert(preset.type == "name", "unexpected preset type")
59
+ // whole json, including type
60
+ presetResource = smart.createJsonResource(preset)
61
+ }
62
+
63
+ limitInput := args.limitInput
64
+ isLibraryFileGzipped := false
65
+
66
+ library := false
67
+ species := args.species
68
+ libraryImportHandle := undefined
69
+ if !is_undefined(args.resolvedLibraryInput) {
70
+ library = args.resolvedLibraryInput.data
71
+ librarySpec := args.resolvedLibraryInput.spec
72
+ species = librarySpec["annotations"]["pl7.app/species"]
73
+ } else if !is_undefined(args.libraryFile) {
74
+ fImport := file.importFile(args.libraryFile)
75
+ libraryImportHandle = fImport.handle
76
+ library = fImport.file
77
+ species = args.customSpecies
78
+ isLibraryFileGzipped = args.isLibraryFileGzipped
79
+ }
80
+
81
+ input := wf.resolve(inputRef)
82
+
83
+ //ll.print("__THE_LOG__ " +input)
84
+
85
+ presetInfoResult := render.create(calculatePresetInfoTpl, {
86
+ preset: presetResource,
87
+ params: {
88
+ species: species
89
+ }
90
+ })
91
+
92
+ presetContent := presetInfoResult.output("preset", 24 * 60 * 60 * 1000)
93
+ presetSpecForBack := presetInfoResult.output("presetSpecForBack", 24 * 60 * 60 * 1000)
94
+
95
+ // calculating chains
96
+ chains := [
97
+ { name: "IGH", mixcrChain: "IGH" },
98
+ { name: "IGKL", mixcrChain: "IGK,IGL" },
99
+ { name: "TRA", mixcrChain: "TRA" },
100
+ { name: "TRB", mixcrChain: "TRB" },
101
+ { name: "TRD", mixcrChain: "TRD" },
102
+ { name: "TRG", mixcrChain: "TRG" } ]
103
+
104
+ runMixcr := render.createEphemeral(processTpl, {
105
+ inputSpec: input.getFutureInputField("spec"),
106
+ inputData: input.getFutureInputField("data"),
107
+
108
+ preset: presetResource,
109
+ presetSpecForBack: presetSpecForBack,
110
+ presetContent: presetContent,
111
+
112
+ library: library,
113
+
114
+ params: smart.createJsonResource({
115
+ species: species,
116
+ chains: chains,
117
+ limitInput: limitInput,
118
+ blockId: blockId,
119
+ presetCommonName: args.presetCommonName,
120
+ isLibraryFileGzipped: isLibraryFileGzipped
121
+ })
122
+ })
123
+
124
+ exports := {
125
+ qc: {
126
+ spec: runMixcr.output("qc.spec"),
127
+ data: runMixcr.output("qc.data")
128
+ },
129
+ reports: {
130
+ spec: runMixcr.output("reports.spec"),
131
+ data: runMixcr.output("reports.data")
132
+ },
133
+ clones: runMixcr.output("clonotypes"),
134
+ clns: {
135
+ spec: runMixcr.output("clns.spec"),
136
+ data: runMixcr.output("clns.data")
137
+ }
138
+ }
139
+
140
+ outputs := {
141
+ qc: pframes.exportColumnData(runMixcr.output("qc.data")),
142
+ reports: pframes.exportColumnData(runMixcr.output("reports.data")),
143
+ logs: runMixcr.output("logs.data"),
144
+ clonotypes: pframes.exportFrame(runMixcr.output("clonotypes")),
145
+ clns: runMixcr.output("clns.data"),
146
+ fileImports: smart.createMapResource(maps.mapValues(fileImports, func(im) {
147
+ return im.handle
148
+ }))
149
+ }
150
+
151
+ if !is_undefined(libraryImportHandle) {
152
+ outputs.libraryImportHandle = libraryImportHandle
153
+ }
154
+
155
+ return {
156
+ outputs: outputs,
157
+ exports: exports
158
+ }
159
+ })
@@ -0,0 +1,171 @@
1
+ // mixcr analyze
2
+
3
+ self := import("@platforma-sdk/workflow-tengo:tpl")
4
+ smart := import("@platforma-sdk/workflow-tengo:smart")
5
+ pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
6
+ ll := import("@platforma-sdk/workflow-tengo:ll")
7
+ exec := import("@platforma-sdk/workflow-tengo:exec")
8
+ assets := import("@platforma-sdk/workflow-tengo:assets")
9
+ render := import("@platforma-sdk/workflow-tengo:render")
10
+ pframes := import("@platforma-sdk/workflow-tengo:pframes")
11
+ pcolumn := import("@platforma-sdk/workflow-tengo:pframes.pcolumn")
12
+ xsv := import("@platforma-sdk/workflow-tengo:pframes.xsv")
13
+ file := import("@platforma-sdk/workflow-tengo:file")
14
+ times := import("times")
15
+
16
+ json := import("json")
17
+
18
+ self.defineOutputs("qc", "reports", "log", "clns")
19
+
20
+ mixcrSw := assets.importSoftware("@platforma-open/milaboratories.software-mixcr:main")
21
+
22
+ progressPrefix := "[==PROGRESS==]"
23
+
24
+ self.body(func(inputs) {
25
+ inputData := inputs[pConstants.VALUE_FIELD_NAME]
26
+ aggregationAxesNames := inputs[pConstants.AGGREGATION_AXES_NAMES_FIELD_NAME]
27
+
28
+ preset := inputs.preset
29
+ library := inputs.library
30
+
31
+ params := inputs.params
32
+ species := params.species
33
+ limitInput := params.limitInput
34
+ fileExtension := params.fileExtension
35
+ reports := params.reports
36
+ isLibraryFileGzipped := params.isLibraryFileGzipped
37
+
38
+ presetContent := inputs.presetContent.getDataAsJson()
39
+
40
+ if !is_map(presetContent) {
41
+ ll.panic("malformed presetContent %v", presetContent)
42
+ }
43
+
44
+ inputDataMeta := inputData.getDataAsJson()
45
+
46
+ hasAssembleContigs := false
47
+ hasAssembleCells := false
48
+ for stage in presetContent.analysisStages {
49
+ if stage == "assembleContigs" {
50
+ hasAssembleContigs = true
51
+ } else if stage == "assembleCells" {
52
+ hasAssembleCells = true
53
+ }
54
+ }
55
+
56
+ clnsFileName := "result.clns"
57
+ if hasAssembleContigs {
58
+ clnsFileName = "result.contigs.clns"
59
+ }
60
+ if hasAssembleCells {
61
+ clnsFileName = "result.assembledCells.clns"
62
+ }
63
+
64
+ mixcrCmdBuilder := exec.builder().
65
+ printErrStreamToStdout().
66
+ env("MI_PROGRESS_PREFIX", progressPrefix).
67
+ software(mixcrSw).
68
+ secret("MI_LICENSE", "MI_LICENSE").
69
+ arg("analyze")
70
+
71
+ if !is_undefined(limitInput) {
72
+ mixcrCmdBuilder.arg("--limit-input").arg(string(limitInput))
73
+ }
74
+
75
+ if library {
76
+ mixcrCmdBuilder.arg("--species").arg(species)
77
+ if isLibraryFileGzipped {
78
+ mixcrCmdBuilder.arg("--library").arg("library.json.gz").
79
+ addFile("library.json.gz", library)
80
+ } else {
81
+ mixcrCmdBuilder.arg("--library").arg("library.json").
82
+ addFile("library.json", library)
83
+ }
84
+ } else if !is_undefined(species) {
85
+ species = params.species
86
+ mixcrCmdBuilder.arg("--species").arg(species)
87
+ }
88
+
89
+ if smart.isResource(preset) /* file */ {
90
+ mixcrCmdBuilder.
91
+ arg("local#input_preset").
92
+ addFile("input_preset.yaml", preset)
93
+ } else {
94
+ ll.assert(!is_undefined(preset.name), "undefined preset name")
95
+ mixcrCmdBuilder.
96
+ arg(preset.name)
97
+ }
98
+
99
+ if inputDataMeta.keyLength == 0 {
100
+ ll.assert(aggregationAxesNames == [], "unexpected aggregation axes names")
101
+ inputFile := inputData.inputs()["[]"]
102
+ ll.assert(!is_undefined(inputFile), "unexpected agg group structure")
103
+ inputFileName := "input." + fileExtension
104
+ mixcrCmdBuilder.addFile(inputFileName, inputFile)
105
+ mixcrCmdBuilder.arg(inputFileName)
106
+ } else if inputDataMeta.keyLength == 1 {
107
+ ll.assert(aggregationAxesNames == ["pl7.app/sequencing/readIndex"], "unexpected aggregation axes names")
108
+ for sKey, inputFile in inputData.inputs() {
109
+ key := json.decode(sKey)
110
+ if len(key) != 1 {
111
+ ll.panic("malformed key: %v", sKey)
112
+ }
113
+ r := key[0]
114
+ if (r[0] != 'R' && r[0] != "I") || (r[1] != '1' && r[1] != '2') || len(r) != 2 {
115
+ ll.panic("malformed read index: %v", r)
116
+ }
117
+ mixcrCmdBuilder.addFile("input_" + r + "." + fileExtension, inputFile)
118
+ }
119
+ mixcrCmdBuilder.arg("input_{{R}}." + fileExtension)
120
+ } else if inputDataMeta.keyLength == 2 {
121
+ ll.assert(aggregationAxesNames == ["pl7.app/sequencing/lane", "pl7.app/sequencing/readIndex"], "unexpected aggregation axes names")
122
+ for sKey, inputFile in inputData.inputs() {
123
+ key := json.decode(sKey)
124
+ if len(key) != 2 {
125
+ ll.panic("malformed key: %v", sKey)
126
+ }
127
+ lane := key[0]
128
+ r := key[1]
129
+ if (r[0] != 'R' && r[0] != "I") || (r[1] != '1' && r[1] != '2') || len(r) != 2 {
130
+ ll.panic("malformed read index: %v", r)
131
+ }
132
+ if is_undefined(int(lane)) {
133
+ ll.panic("malformed lane: %v", lane)
134
+ }
135
+ mixcrCmdBuilder.addFile("input_L" + lane + "_" + r + "." + fileExtension, inputFile)
136
+ }
137
+ mixcrCmdBuilder.arg("input_L{{n}}_{{R}}." + fileExtension)
138
+ } else {
139
+ ll.panic("too many axes / not supported")
140
+ }
141
+
142
+ mixcrCmdBuilder.arg("result")
143
+
144
+ mixcrCmdBuilder.saveFile("result.qc.json")
145
+ mixcrCmdBuilder.saveFile(clnsFileName)
146
+
147
+ for report in reports {
148
+ mixcrCmdBuilder.saveFile(report.fileJson)
149
+ mixcrCmdBuilder.saveFile(report.fileTxt)
150
+ }
151
+
152
+ mixcrCmdBuilder = mixcrCmdBuilder.cache(48 * times.hour)
153
+ mixcrCmd := mixcrCmdBuilder.run()
154
+
155
+ // collecting results
156
+
157
+ reportsMap := pcolumn.resourceMapBuilder( /* keyLength */ 2 )
158
+ for report in reports {
159
+ reportsMap.add([report.id, "json"], mixcrCmd.getFile(report.fileJson))
160
+ reportsMap.add([report.id, "txt"], mixcrCmd.getFile(report.fileTxt))
161
+ }
162
+
163
+ result := {
164
+ qc: mixcrCmd.getFile("result.qc.json"),
165
+ log: mixcrCmd.getStdoutStream(),
166
+ reports: reportsMap.build(),
167
+ clns: mixcrCmd.getFile(clnsFileName)
168
+ }
169
+
170
+ return result
171
+ })
@@ -0,0 +1,77 @@
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
+ assets := import("@platforma-sdk/workflow-tengo:assets")
5
+ exec := import("@platforma-sdk/workflow-tengo:exec")
6
+
7
+ json := import("json")
8
+
9
+ self.defineOutputs("tsv")
10
+
11
+ mixcrSw := assets.importSoftware("@platforma-open/milaboratories.software-mixcr:low-memory")
12
+ ptransformSw := assets.importSoftware("@platforma-open/milaboratories.software-ptransform:main")
13
+
14
+ self.body(func(inputs) {
15
+ clnsFile := inputs[pConstants.VALUE_FIELD_NAME]
16
+
17
+ params := inputs.params
18
+ chains := params.chains
19
+ exportArgs := params.exportArgs
20
+
21
+ clonotypeKeyColumns := params.clonotypeKeyColumns
22
+
23
+ // Exporting clones from clns file
24
+
25
+ mixcrCmdBuilder := exec.builder().
26
+ printErrStreamToStdout().
27
+ software(mixcrSw).
28
+ secret("MI_LICENSE", "MI_LICENSE").
29
+ arg("exportClones").
30
+ arg("--dont-split-files").
31
+ arg("--chains").arg(chains)
32
+
33
+ for argGrp in exportArgs {
34
+ for arg in argGrp {
35
+ mixcrCmdBuilder.arg(arg)
36
+ }
37
+ }
38
+
39
+ mixcrCmd := mixcrCmdBuilder.
40
+ arg("clones.clns").
41
+ addFile("clones.clns", clnsFile).
42
+ arg("clones.tsv").
43
+ saveFile("clones.tsv").
44
+ run()
45
+
46
+ unprocessedTsv := mixcrCmd.getFile("clones.tsv")
47
+
48
+ if is_undefined(clonotypeKeyColumns) {
49
+ return {
50
+ tsv: unprocessedTsv
51
+ }
52
+ } else {
53
+ // Adding clonotypeKey column
54
+ pWorkflow := {
55
+ steps: [ {
56
+ type: "combine_columns_as_json",
57
+ src: clonotypeKeyColumns,
58
+ dst: "clonotypeKey"
59
+ } ]
60
+ }
61
+
62
+ aggregateCmd := exec.builder().
63
+ printErrStreamToStdout().
64
+ software(ptransformSw).
65
+ arg("--workflow").arg("wf.json").
66
+ writeFile("wf.json", json.encode(pWorkflow)).
67
+ arg("input.tsv").addFile("input.tsv", unprocessedTsv).
68
+ arg("output.tsv").saveFile("output.tsv").
69
+ run()
70
+
71
+ processedTsv := aggregateCmd.getFile("output.tsv")
72
+
73
+ return {
74
+ tsv: processedTsv
75
+ }
76
+ }
77
+ })
@@ -0,0 +1,70 @@
1
+ // pre-run
2
+
3
+ wf := import("@platforma-sdk/workflow-tengo:workflow")
4
+ maps := import("@platforma-sdk/workflow-tengo:maps")
5
+ render := import("@platforma-sdk/workflow-tengo:render")
6
+ ll := import("@platforma-sdk/workflow-tengo:ll")
7
+ smart := import("@platforma-sdk/workflow-tengo:smart")
8
+ assets := import("@platforma-sdk/workflow-tengo:assets")
9
+ file := import("@platforma-sdk/workflow-tengo:file")
10
+
11
+ listPresetsTpl := assets.importTemplate(":list-presets")
12
+ calculatePresetInfoTpl := assets.importTemplate(":calculate-preset-info")
13
+
14
+ wf.body(func(args) {
15
+
16
+ // (begin) @TODO To be moved to SDK / Model
17
+
18
+ fileImports := {}
19
+
20
+ importFile := func(importHandle) {
21
+ fromMap := fileImports[importHandle]
22
+ if fromMap == undefined {
23
+ fImport := file.importFile(importHandle)
24
+ fileImports[importHandle] = fImport
25
+ return fImport.file
26
+ } else {
27
+ return fromMap.file
28
+ }
29
+ }
30
+
31
+ // (end)
32
+
33
+ outputs := {}
34
+
35
+ listPresets := render.create(listPresetsTpl, {})
36
+ outputs.presets = listPresets.output("presets", 24 * 60 * 60 * 1000)
37
+
38
+ if !is_undefined(args.preset) {
39
+
40
+ preset := args.preset
41
+
42
+ // file or json with preset name
43
+ presetResource := undefined
44
+
45
+ if preset.type == "file" {
46
+ //presetResource = importFile(preset.file)
47
+ presetResource = undefined
48
+ } else {
49
+ ll.assert(preset.type == "name", "unexpected preset type")
50
+ // whole json, including type
51
+ presetResource = smart.createJsonResource(preset)
52
+ presetInfoResult := render.create(calculatePresetInfoTpl, {
53
+ preset: presetResource,
54
+ params: {
55
+ species: args.species
56
+ }
57
+ })
58
+ outputs.preset = presetInfoResult.output("presetSpecForBack", 24 * 60 * 60 * 1000)
59
+ }
60
+ }
61
+
62
+ outputs.fileImports = smart.createMapResource(maps.mapValues(fileImports, func(im) {
63
+ return im.handle
64
+ }))
65
+
66
+ return {
67
+ outputs: outputs,
68
+ exports: {}
69
+ }
70
+ })