@platforma-open/milaboratories.top-antibodies.workflow 1.7.0 → 1.8.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.
- package/.turbo/turbo-build.log +5 -1
- package/CHANGELOG.md +18 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/tengo/tpl/filter-and-sample.plj.gz +0 -0
- package/dist/tengo/tpl/main.plj.gz +0 -0
- package/dist/tengo/tpl/prerun.plj.gz +0 -0
- package/package.json +6 -6
- package/src/filter-and-sample.tpl.tengo +80 -0
- package/src/main.tpl.tengo +121 -103
- package/src/prerun.tpl.tengo +204 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
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.
|
|
3
|
+
> @platforma-open/milaboratories.top-antibodies.workflow@1.8.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
|
+
Processing "src/filter-and-sample.tpl.tengo"...
|
|
6
7
|
Processing "src/main.tpl.tengo"...
|
|
7
8
|
Processing "src/pf-spectratype-conv.lib.tengo"...
|
|
8
9
|
Processing "src/pf-vj-usage-conv.lib.tengo"...
|
|
10
|
+
Processing "src/prerun.tpl.tengo"...
|
|
9
11
|
Processing "src/sampled-cols-conv.lib.tengo"...
|
|
10
12
|
No syntax errors found.
|
|
11
13
|
info: Compiling 'dist'...
|
|
12
14
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/pf-spectratype-conv.lib.tengo
|
|
13
15
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/pf-vj-usage-conv.lib.tengo
|
|
14
16
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/lib/sampled-cols-conv.lib.tengo
|
|
17
|
+
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/tpl/filter-and-sample.plj.gz
|
|
18
|
+
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/tpl/prerun.plj.gz
|
|
15
19
|
info: - writing /home/runner/work/antibody-tcr-lead-selection/antibody-tcr-lead-selection/workflow/dist/tengo/tpl/main.plj.gz
|
|
16
20
|
info: Template Pack build done.
|
|
17
21
|
info: Template Pack build done.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @platforma-open/milaboratories.top-antibodies.workflow
|
|
2
2
|
|
|
3
|
+
## 1.8.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7397001: Remove typo
|
|
8
|
+
|
|
9
|
+
## 1.8.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- a435169: Move filters to settings and add prerun
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [a435169]
|
|
18
|
+
- @platforma-open/milaboratories.top-antibodies.sample-clonotypes@1.1.0
|
|
19
|
+
- @platforma-open/milaboratories.top-antibodies.spectratype@1.4.0
|
|
20
|
+
|
|
3
21
|
## 1.7.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
module.exports = { Templates: {
|
|
2
|
+
'filter-and-sample': { type: 'from-file', path: require.resolve('./tengo/tpl/filter-and-sample.plj.gz') },
|
|
3
|
+
'prerun': { type: 'from-file', path: require.resolve('./tengo/tpl/prerun.plj.gz') },
|
|
2
4
|
'main': { type: 'from-file', path: require.resolve('./tengo/tpl/main.plj.gz') }
|
|
3
5
|
}};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
declare type TemplateFromFile = { readonly type: "from-file"; readonly path: string; };
|
|
2
|
-
declare type TplName = "main";
|
|
2
|
+
declare type TplName = "filter-and-sample" | "prerun" | "main";
|
|
3
3
|
declare const Templates: Record<TplName, TemplateFromFile>;
|
|
4
4
|
export { Templates };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
export const Templates = {
|
|
3
|
+
'filter-and-sample': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/filter-and-sample.plj.gz') },
|
|
4
|
+
'prerun': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/prerun.plj.gz') },
|
|
3
5
|
'main': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/main.plj.gz') }
|
|
4
6
|
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-open/milaboratories.top-antibodies.workflow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Block Workflow",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@platforma-sdk/workflow-tengo": "^4.
|
|
8
|
-
"@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "1.0
|
|
9
|
-
"@platforma-open/milaboratories.top-antibodies.spectratype": "1.
|
|
7
|
+
"@platforma-sdk/workflow-tengo": "^4.9.2",
|
|
8
|
+
"@platforma-open/milaboratories.top-antibodies.sample-clonotypes": "1.1.0",
|
|
9
|
+
"@platforma-open/milaboratories.top-antibodies.spectratype": "1.4.0",
|
|
10
10
|
"@platforma-open/milaboratories.top-antibodies.umap": "1.0.3"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@platforma-sdk/tengo-builder": "^2.1.
|
|
14
|
-
"@platforma-sdk/test": "1.
|
|
13
|
+
"@platforma-sdk/tengo-builder": "^2.1.12",
|
|
14
|
+
"@platforma-sdk/test": "^1.38.0",
|
|
15
15
|
"vitest": "^2.1.8"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Template for clonotype filtering
|
|
2
|
+
self := import("@platforma-sdk/workflow-tengo:tpl")
|
|
3
|
+
exec := import("@platforma-sdk/workflow-tengo:exec")
|
|
4
|
+
assets := import("@platforma-sdk/workflow-tengo:assets")
|
|
5
|
+
pframes := import("@platforma-sdk/workflow-tengo:pframes")
|
|
6
|
+
xsv := import("@platforma-sdk/workflow-tengo:pframes.xsv")
|
|
7
|
+
render := import("@platforma-sdk/workflow-tengo:render")
|
|
8
|
+
sampledColsConv := import(":sampled-cols-conv")
|
|
9
|
+
json := import("json")
|
|
10
|
+
|
|
11
|
+
self.defineOutputs("sampledRows", "finalClonotypesCsv")
|
|
12
|
+
|
|
13
|
+
self.body(func(inputs) {
|
|
14
|
+
|
|
15
|
+
cloneTable := inputs.cloneTable
|
|
16
|
+
datasetSpec := inputs.datasetSpec
|
|
17
|
+
filterMap := inputs.filterMap
|
|
18
|
+
topClonotypes := inputs.topClonotypes
|
|
19
|
+
|
|
20
|
+
outputs := {}
|
|
21
|
+
finalClonotypesCsv := undefined
|
|
22
|
+
|
|
23
|
+
// Run filtering script
|
|
24
|
+
filterResult := exec.builder().
|
|
25
|
+
software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.sample-clonotypes:filter")).
|
|
26
|
+
mem("16GiB").
|
|
27
|
+
cpu(1).
|
|
28
|
+
addFile("clonotypes.csv", cloneTable).
|
|
29
|
+
arg("--csv").arg("clonotypes.csv").
|
|
30
|
+
arg("--out").arg("filteredClonotypes.csv").
|
|
31
|
+
arg("--filter-map").arg(string(json.encode(filterMap))).
|
|
32
|
+
saveFile("filteredClonotypes.csv").
|
|
33
|
+
printErrStreamToStdout().
|
|
34
|
+
saveStdoutContent().
|
|
35
|
+
cache(24 * 60 * 60 * 1000).
|
|
36
|
+
run()
|
|
37
|
+
|
|
38
|
+
// Save filtered CSV file
|
|
39
|
+
filteredClonotypesCsv := filterResult.getFile("filteredClonotypes.csv")
|
|
40
|
+
|
|
41
|
+
// Store outputs
|
|
42
|
+
sampledColsParams := sampledColsConv.getColumns(datasetSpec)
|
|
43
|
+
filteredClonotypesPf := xsv.importFile(filteredClonotypesCsv, "csv", sampledColsParams,
|
|
44
|
+
{cpu: 1, mem: "16GiB"})
|
|
45
|
+
|
|
46
|
+
// Prepare outputs in case there is no top ranking
|
|
47
|
+
outputs["sampledRows"] = pframes.exportFrame(filteredClonotypesPf)
|
|
48
|
+
finalClonotypesCsv = filteredClonotypesCsv
|
|
49
|
+
|
|
50
|
+
if topClonotypes != undefined {
|
|
51
|
+
|
|
52
|
+
////////// Top Clonotypes Sampling //////////
|
|
53
|
+
// Run sampling script on filtered data
|
|
54
|
+
sampleClones := exec.builder().
|
|
55
|
+
software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.sample-clonotypes:main")).
|
|
56
|
+
mem("16GiB").
|
|
57
|
+
cpu(1).
|
|
58
|
+
addFile("filteredClonotypes.csv", filteredClonotypesCsv).
|
|
59
|
+
arg("--csv").arg("filteredClonotypes.csv").
|
|
60
|
+
arg("--n").arg(string(topClonotypes)).
|
|
61
|
+
arg("--out").arg("sampledClonotypes_top.csv").
|
|
62
|
+
saveFile("sampledClonotypes_top.csv").
|
|
63
|
+
printErrStreamToStdout().
|
|
64
|
+
saveStdoutContent().
|
|
65
|
+
cache(24 * 60 * 60 * 1000).
|
|
66
|
+
run()
|
|
67
|
+
|
|
68
|
+
// Save top clonotypes CSV file
|
|
69
|
+
finalClonotypesCsv = sampleClones.getFile("sampledClonotypes_top.csv")
|
|
70
|
+
|
|
71
|
+
// Store outputs
|
|
72
|
+
sampledColumnsPf := xsv.importFile(finalClonotypesCsv, "csv", sampledColsParams,
|
|
73
|
+
{cpu: 1, mem: "16GiB"})
|
|
74
|
+
outputs["sampledRows"] = pframes.exportFrame(sampledColumnsPf)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
outputs["finalClonotypesCsv"] = finalClonotypesCsv
|
|
78
|
+
|
|
79
|
+
return outputs
|
|
80
|
+
})
|
package/src/main.tpl.tengo
CHANGED
|
@@ -8,46 +8,49 @@ sampledColsConv := import(":sampled-cols-conv")
|
|
|
8
8
|
spectratypeConv := import(":pf-spectratype-conv")
|
|
9
9
|
vjUsageConv := import(":pf-vj-usage-conv")
|
|
10
10
|
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
11
|
+
render := import("@platforma-sdk/workflow-tengo:render")
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
// We need a table with cluster ID (optional) | clonotype id | selected ranking columns
|
|
14
|
-
|
|
15
|
-
bundleBuilder := wf.createPBundleBuilder()
|
|
16
|
-
bundleBuilder.ignoreMissingDomains() // to make query work for both bulk and single cell data
|
|
17
|
-
bundleBuilder.addAnchor("main", args.inputAnchor)
|
|
18
|
-
|
|
19
|
-
if len(args.rankingOrder) > 0 {
|
|
20
|
-
for col in args.rankingOrder {
|
|
21
|
-
bundleBuilder.addAnchor(col.value.anchorName, col.value.anchorRef)
|
|
22
|
-
bundleBuilder.addSingle(col.value.column)
|
|
23
|
-
}
|
|
24
|
-
} else {
|
|
25
|
-
bundleBuilder.addAnchor(args.rankingOrderDefault.value.anchorName,
|
|
26
|
-
args.rankingOrderDefault.value.anchorRef)
|
|
27
|
-
bundleBuilder.addSingle(args.rankingOrderDefault.value.column)
|
|
28
|
-
}
|
|
29
|
-
|
|
13
|
+
filterAndSampleTpl := assets.importTemplate(":filter-and-sample")
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
axes: [{ anchor: "main", idx: 1 }], // this will do partial axes match (unlike in the model)
|
|
34
|
-
annotations: { "pl7.app/isLinkerColumn": "true" },
|
|
35
|
-
partialAxesMatch: true
|
|
36
|
-
}, "linkers")
|
|
37
|
-
|
|
38
|
-
// Add full aa sequence column
|
|
39
|
-
bundleBuilder.addMulti({
|
|
40
|
-
axes: [{ anchor: "main", idx: 1 }],
|
|
41
|
-
annotations: {
|
|
42
|
-
"pl7.app/vdj/isAssemblingFeature": "true",
|
|
43
|
-
"pl7.app/vdj/isMainSequence": "true"
|
|
44
|
-
},
|
|
45
|
-
domain: {
|
|
46
|
-
"pl7.app/alphabet": "aminoacid"
|
|
47
|
-
}
|
|
48
|
-
}, "aaSequence")
|
|
15
|
+
// Set prerun template for clonotype filtering
|
|
16
|
+
wf.setPreRun(assets.importTemplate(":prerun"))
|
|
49
17
|
|
|
50
|
-
|
|
18
|
+
wf.prepare(func(args){
|
|
19
|
+
// We need a table with cluster ID (optional) | clonotype id | selected ranking columns
|
|
20
|
+
bundleBuilder := wf.createPBundleBuilder()
|
|
21
|
+
bundleBuilder.ignoreMissingDomains() // to make query work for both bulk and single cell data
|
|
22
|
+
bundleBuilder.addAnchor("main", args.inputAnchor)
|
|
23
|
+
|
|
24
|
+
if len(args.rankingOrder) > 0 {
|
|
25
|
+
for col in args.rankingOrder {
|
|
26
|
+
bundleBuilder.addAnchor(col.value.anchorName, col.value.anchorRef)
|
|
27
|
+
bundleBuilder.addSingle(col.value.column)
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
bundleBuilder.addAnchor(args.rankingOrderDefault.value.anchorName,
|
|
31
|
+
args.rankingOrderDefault.value.anchorRef)
|
|
32
|
+
bundleBuilder.addSingle(args.rankingOrderDefault.value.column)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Load filter columns
|
|
36
|
+
if len(args.filters) > 0 {
|
|
37
|
+
for filter in args.filters {
|
|
38
|
+
if filter.value != undefined {
|
|
39
|
+
bundleBuilder.addAnchor(filter.value.anchorName, filter.value.anchorRef)
|
|
40
|
+
bundleBuilder.addSingle(filter.value.column)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Add linker column
|
|
47
|
+
bundleBuilder.addMulti({
|
|
48
|
+
axes: [{ anchor: "main", idx: 1 }], // this will do partial axes match (unlike in the model)
|
|
49
|
+
annotations: { "pl7.app/isLinkerColumn": "true" },
|
|
50
|
+
partialAxesMatch: true
|
|
51
|
+
}, "linkers")
|
|
52
|
+
|
|
53
|
+
// Add CDR3 sequences
|
|
51
54
|
bundleBuilder.addMulti({
|
|
52
55
|
axes: [{ anchor: "main", idx: 1 }], // Clonotype axis
|
|
53
56
|
name: "pl7.app/vdj/sequence",
|
|
@@ -74,10 +77,10 @@ wf.prepare(func(args){
|
|
|
74
77
|
"pl7.app/vdj/reference": "JGene"
|
|
75
78
|
}
|
|
76
79
|
}, "JGenes")
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
columns: bundleBuilder.build()
|
|
83
|
+
}
|
|
81
84
|
})
|
|
82
85
|
|
|
83
86
|
wf.body(func(args) {
|
|
@@ -94,90 +97,105 @@ wf.body(func(args) {
|
|
|
94
97
|
// output containers
|
|
95
98
|
outputs := {}
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
// Get filtered clonotypes from prerun
|
|
101
|
+
// Build clonotype table
|
|
102
|
+
cloneTable := pframes.csvFileBuilder()
|
|
103
|
+
cloneTable.setAxisHeader(datasetSpec.axesSpec[1].name, "clonotypeKey")
|
|
104
|
+
|
|
105
|
+
// Add Filters to table
|
|
106
|
+
addedAxes := []
|
|
107
|
+
filterMap := {}
|
|
108
|
+
if len(args.filters) > 0 {
|
|
109
|
+
for i, filter in args.filters {
|
|
110
|
+
if filter.value != undefined {
|
|
111
|
+
// Columns added here might also be in ranking list, so we add default IDs
|
|
112
|
+
cloneTable.add(columns.getColumn(filter.value.column),
|
|
113
|
+
{header: "Filter_" + string(i), id: "filter_" + string(i)})
|
|
114
|
+
// Store reference value and filter type associated to this column
|
|
115
|
+
filterMap["Filter_" + string(i)] = filter.filter
|
|
109
116
|
|
|
110
117
|
// If column does not have main anchor axis we have to include theirs
|
|
111
|
-
colsSpec := columns.getSpec(
|
|
118
|
+
colsSpec := columns.getSpec(filter.value.column)
|
|
112
119
|
axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
|
|
113
120
|
if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
|
|
114
121
|
for na, ax in colsSpec.axesSpec {
|
|
115
122
|
if ax.name != datasetSpec.axesSpec[1].name {
|
|
116
123
|
cloneTable.setAxisHeader(ax.name, "cluster_" + string(i) + string(na))
|
|
124
|
+
addedAxes = slices.append(addedAxes, ax.name)
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add ranking columns to table
|
|
133
|
+
if len(args.rankingOrder) > 0 {
|
|
134
|
+
for i, col in args.rankingOrder {
|
|
135
|
+
cloneTable.add(columns.getColumn(col.value.column), {header: "Col" + string(i)})
|
|
124
136
|
|
|
125
137
|
// If column does not have main anchor axis we have to include theirs
|
|
126
|
-
colsSpec := columns.getSpec(
|
|
138
|
+
colsSpec := columns.getSpec(col.value.column)
|
|
127
139
|
axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
|
|
128
140
|
if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
|
|
129
141
|
for na, ax in colsSpec.axesSpec {
|
|
130
|
-
if ax.name != datasetSpec.axesSpec[1].name {
|
|
142
|
+
if ax.name != datasetSpec.axesSpec[1].name && !slices.hasElement(addedAxes, ax.name) {
|
|
131
143
|
cloneTable.setAxisHeader(ax.name, "cluster_" + string(i) + string(na))
|
|
132
144
|
}
|
|
133
145
|
}
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
cloneTable.
|
|
147
|
-
cloneTable.setAxisHeader(col.spec.axesSpec[1].name, "cluster_" + string(i))
|
|
148
|
-
linkerAxisSpec["cluster_" + string(i)] = col.spec.axesSpec[1]
|
|
148
|
+
} else {
|
|
149
|
+
i := 0
|
|
150
|
+
cloneTable.add(columns.getColumn(args.rankingOrderDefault.value.column), {header: "Col" + string(i)})
|
|
151
|
+
|
|
152
|
+
// If column does not have main anchor axis we have to include theirs
|
|
153
|
+
colsSpec := columns.getSpec(args.rankingOrderDefault.value.column)
|
|
154
|
+
axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
|
|
155
|
+
if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
|
|
156
|
+
for na, ax in colsSpec.axesSpec {
|
|
157
|
+
if ax.name != datasetSpec.axesSpec[1].name {
|
|
158
|
+
cloneTable.setAxisHeader(ax.name, "cluster_" + string(i) + string(na))
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
|
-
}
|
|
152
|
-
cloneTable.mem("16GiB")
|
|
153
|
-
cloneTable.cpu(1)
|
|
154
|
-
cloneTable = cloneTable.build()
|
|
155
|
-
|
|
156
|
-
// Run sampling script
|
|
157
|
-
sampleClones := exec.builder().
|
|
158
|
-
software(assets.importSoftware("@platforma-open/milaboratories.top-antibodies.sample-clonotypes:main")).
|
|
159
|
-
mem("16GiB").
|
|
160
|
-
cpu(1).
|
|
161
|
-
addFile("filteredClonotypes.csv", cloneTable).
|
|
162
|
-
arg("--csv").arg("filteredClonotypes.csv").
|
|
163
|
-
arg("--n").arg(string(topClonotypes)).
|
|
164
|
-
arg("--out").arg("sampledClonotypes.csv").
|
|
165
|
-
saveFile("sampledClonotypes_top.csv").
|
|
166
|
-
printErrStreamToStdout().
|
|
167
|
-
saveStdoutContent().
|
|
168
|
-
cache(24 * 60 * 60 * 1000).
|
|
169
|
-
run()
|
|
170
|
-
|
|
171
|
-
// Save top clonotypes CSV file
|
|
172
|
-
topClonotypesCsv = sampleClones.getFile("sampledClonotypes_top.csv")
|
|
173
|
-
|
|
174
|
-
// Store outputs
|
|
175
|
-
sampledColsParams := sampledColsConv.getColumns(datasetSpec)
|
|
176
|
-
sampledColumnsPf := xsv.importFile(topClonotypesCsv, "csv", sampledColsParams,
|
|
177
|
-
{cpu: 1, mem: "16GiB"})
|
|
178
|
-
outputs["sampledRows"] = pframes.exportFrame(sampledColumnsPf)
|
|
161
|
+
}
|
|
179
162
|
}
|
|
180
163
|
|
|
164
|
+
// Get linker columns if needed
|
|
165
|
+
linkerAxisSpec := {}
|
|
166
|
+
if len(columns.getColumns("linkers")) > 0 {
|
|
167
|
+
for i, col in columns.getColumns("linkers") {
|
|
168
|
+
if datasetSpec.axesSpec[1].name == col.spec.axesSpec[1].name {
|
|
169
|
+
cloneTable.add(col, {header: "linker." + string(i)})
|
|
170
|
+
cloneTable.setAxisHeader(col.spec.axesSpec[0].name, "cluster_" + string(i))
|
|
171
|
+
linkerAxisSpec["cluster_" + string(i)] = col.spec.axesSpec[0]
|
|
172
|
+
} else if datasetSpec.axesSpec[1].name == col.spec.axesSpec[0].name {
|
|
173
|
+
cloneTable.add(col, {header: "linker." + string(i)})
|
|
174
|
+
cloneTable.setAxisHeader(col.spec.axesSpec[1].name, "cluster_" + string(i))
|
|
175
|
+
linkerAxisSpec["cluster_" + string(i)] = col.spec.axesSpec[1]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
cloneTable.mem("16GiB")
|
|
180
|
+
cloneTable.cpu(1)
|
|
181
|
+
cloneTable = cloneTable.build()
|
|
182
|
+
|
|
183
|
+
// Use ender.create to call the filter-clonotypes template
|
|
184
|
+
filterResult := render.create(filterAndSampleTpl, {
|
|
185
|
+
inputAnchor: args.inputAnchor,
|
|
186
|
+
cloneTable: cloneTable,
|
|
187
|
+
rankingOrder: args.rankingOrder,
|
|
188
|
+
rankingOrderDefault: args.rankingOrderDefault,
|
|
189
|
+
filters: args.filters,
|
|
190
|
+
filterMap: filterMap,
|
|
191
|
+
datasetSpec: datasetSpec,
|
|
192
|
+
topClonotypes: args.topClonotypes
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// Get the filtered clonotypes P-frame and CSV from the template result
|
|
196
|
+
finalClonotypesCsv := filterResult.output("finalClonotypesCsv", 24 * 60 * 60 * 1000)
|
|
197
|
+
// outputs["sampledRows"] = filterResult.output("sampledRows", 24 * 60 * 60 * 1000)
|
|
198
|
+
|
|
181
199
|
////////// CDR3 Length Calculation //////////
|
|
182
200
|
|
|
183
201
|
cdr3SeqTable := pframes.tsvFileBuilder()
|
|
@@ -247,10 +265,10 @@ wf.body(func(args) {
|
|
|
247
265
|
arg("--vj_usage_tsv").arg("vj_usage.tsv") // no dot here
|
|
248
266
|
|
|
249
267
|
// Add top clonotypes argument and file to the builder if provided
|
|
250
|
-
if
|
|
268
|
+
if finalClonotypesCsv != undefined {
|
|
251
269
|
cdr3VspectratypeCmd = cdr3VspectratypeCmd.
|
|
252
|
-
arg("--
|
|
253
|
-
addFile("
|
|
270
|
+
arg("--final_clonotypes_csv").arg("finalClonotypes.csv").
|
|
271
|
+
addFile("finalClonotypes.csv", finalClonotypesCsv)
|
|
254
272
|
}
|
|
255
273
|
|
|
256
274
|
cdr3VspectratypeCmd = cdr3VspectratypeCmd. // continue building the command
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Prerun template for clonotype filtering
|
|
2
|
+
wf := import("@platforma-sdk/workflow-tengo:workflow")
|
|
3
|
+
render := import("@platforma-sdk/workflow-tengo:render")
|
|
4
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
5
|
+
assets := import("@platforma-sdk/workflow-tengo:assets")
|
|
6
|
+
pframes := import("@platforma-sdk/workflow-tengo:pframes")
|
|
7
|
+
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
8
|
+
|
|
9
|
+
filterAndSampleTpl := assets.importTemplate(":filter-and-sample")
|
|
10
|
+
|
|
11
|
+
wf.prepare(func(args){
|
|
12
|
+
// We need a table with cluster ID (optional) | clonotype id | selected ranking columns
|
|
13
|
+
bundleBuilder := wf.createPBundleBuilder()
|
|
14
|
+
bundleBuilder.ignoreMissingDomains() // to make query work for both bulk and single cell data
|
|
15
|
+
bundleBuilder.addAnchor("main", args.inputAnchor)
|
|
16
|
+
|
|
17
|
+
if len(args.rankingOrder) > 0 {
|
|
18
|
+
for col in args.rankingOrder {
|
|
19
|
+
// For cases where the user is selecting the table to filter
|
|
20
|
+
if col.value != undefined {
|
|
21
|
+
bundleBuilder.addAnchor(col.value.anchorName, col.value.anchorRef)
|
|
22
|
+
bundleBuilder.addSingle(col.value.column)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
// @TODO: this is a temporal patch for issue where rankingOrderDefault
|
|
27
|
+
// are not defined by the time prerun works
|
|
28
|
+
// prerun sometimes runs before this variable is ready
|
|
29
|
+
if args.rankingOrderDefault.value != undefined {
|
|
30
|
+
bundleBuilder.addAnchor(args.rankingOrderDefault.value.anchorName,
|
|
31
|
+
args.rankingOrderDefault.value.anchorRef)
|
|
32
|
+
bundleBuilder.addSingle(args.rankingOrderDefault.value.column)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Load filter columns
|
|
37
|
+
if len(args.filters) > 0 {
|
|
38
|
+
for filter in args.filters {
|
|
39
|
+
if filter.value != undefined {
|
|
40
|
+
bundleBuilder.addAnchor(filter.value.anchorName, filter.value.anchorRef)
|
|
41
|
+
bundleBuilder.addSingle(filter.value.column)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// Add linker column
|
|
48
|
+
bundleBuilder.addMulti({
|
|
49
|
+
axes: [{ anchor: "main", idx: 1 }], // this will do partial axes match (unlike in the model)
|
|
50
|
+
annotations: { "pl7.app/isLinkerColumn": "true" },
|
|
51
|
+
partialAxesMatch: true
|
|
52
|
+
}, "linkers")
|
|
53
|
+
|
|
54
|
+
// Add CDR3 sequences
|
|
55
|
+
bundleBuilder.addMulti({
|
|
56
|
+
axes: [{ anchor: "main", idx: 1 }], // Clonotype axis
|
|
57
|
+
name: "pl7.app/vdj/sequence",
|
|
58
|
+
domain: {
|
|
59
|
+
"pl7.app/alphabet": "aminoacid",
|
|
60
|
+
"pl7.app/vdj/feature": "CDR3" // Specify CDR3 feature
|
|
61
|
+
}
|
|
62
|
+
}, "cdr3Sequences") // New collection name for CDR3 sequences
|
|
63
|
+
|
|
64
|
+
// Add V gene
|
|
65
|
+
bundleBuilder.addMulti({
|
|
66
|
+
axes: [{ anchor: "main", idx: 1 }], // Clonotype axis
|
|
67
|
+
name: "pl7.app/vdj/geneHit",
|
|
68
|
+
domain: {
|
|
69
|
+
"pl7.app/vdj/reference": "VGene"
|
|
70
|
+
}
|
|
71
|
+
}, "VGenes")
|
|
72
|
+
|
|
73
|
+
// Add J gene
|
|
74
|
+
bundleBuilder.addMulti({
|
|
75
|
+
axes: [{ anchor: "main", idx: 1 }], // Clonotype axis
|
|
76
|
+
name: "pl7.app/vdj/geneHit",
|
|
77
|
+
domain: {
|
|
78
|
+
"pl7.app/vdj/reference": "JGene"
|
|
79
|
+
}
|
|
80
|
+
}, "JGenes")
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
columns: bundleBuilder.build()
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
wf.body(func(args) {
|
|
88
|
+
// output containers
|
|
89
|
+
outputs := {}
|
|
90
|
+
|
|
91
|
+
if !is_undefined(args.inputAnchor) {
|
|
92
|
+
columns := args.columns
|
|
93
|
+
datasetSpec := columns.getSpec(args.inputAnchor)
|
|
94
|
+
|
|
95
|
+
////////// Clonotype Filtering //////////
|
|
96
|
+
// Build clonotype table
|
|
97
|
+
cloneTable := pframes.csvFileBuilder()
|
|
98
|
+
cloneTable.setAxisHeader(datasetSpec.axesSpec[1].name, "clonotypeKey")
|
|
99
|
+
|
|
100
|
+
// Add Filters to table
|
|
101
|
+
addedAxes := []
|
|
102
|
+
filterMap := {}
|
|
103
|
+
if len(args.filters) > 0 {
|
|
104
|
+
for i, filter in args.filters {
|
|
105
|
+
if filter.value != undefined {
|
|
106
|
+
// Columns added here might also be in ranking list, so we add default IDs
|
|
107
|
+
cloneTable.add(columns.getColumn(filter.value.column),
|
|
108
|
+
{header: "Filter_" + string(i), id: "filter_" + string(i)})
|
|
109
|
+
// Store reference value and filter type associated to this column
|
|
110
|
+
filterMap["Filter_" + string(i)] = filter.filter
|
|
111
|
+
|
|
112
|
+
// If column does not have main anchor axis we have to include theirs
|
|
113
|
+
colsSpec := columns.getSpec(filter.value.column)
|
|
114
|
+
axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
|
|
115
|
+
if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
|
|
116
|
+
for na, ax in colsSpec.axesSpec {
|
|
117
|
+
if ax.name != datasetSpec.axesSpec[1].name {
|
|
118
|
+
cloneTable.setAxisHeader(ax.name, "cluster_" + string(i) + string(na))
|
|
119
|
+
addedAxes = append(addedAxes, ax.name)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add ranking columns to table
|
|
128
|
+
if len(args.rankingOrder) > 0 {
|
|
129
|
+
for i, col in args.rankingOrder {
|
|
130
|
+
if col.value != undefined {
|
|
131
|
+
cloneTable.add(columns.getColumn(col.value.column), {header: "Col" + string(i)})
|
|
132
|
+
|
|
133
|
+
// If column does not have main anchor axis we have to include theirs
|
|
134
|
+
colsSpec := columns.getSpec(col.value.column)
|
|
135
|
+
axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
|
|
136
|
+
if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
|
|
137
|
+
for na, ax in colsSpec.axesSpec {
|
|
138
|
+
if ax.name != datasetSpec.axesSpec[1].name && !slices.hasElement(addedAxes, ax.name) {
|
|
139
|
+
cloneTable.setAxisHeader(ax.name, "cluster_" + string(i) + string(na))
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
// @TODO: this is a temporal patch for issue where rankingOrderDefault
|
|
147
|
+
// are not defined by the time prerun works
|
|
148
|
+
if args.rankingOrderDefault.value != undefined {
|
|
149
|
+
i := 0
|
|
150
|
+
cloneTable.add(columns.getColumn(args.rankingOrderDefault.value.column), {header: "Col" + string(i)})
|
|
151
|
+
|
|
152
|
+
// If column does not have main anchor axis we have to include theirs
|
|
153
|
+
colsSpec := columns.getSpec(args.rankingOrderDefault.value.column)
|
|
154
|
+
axesNames := slices.map(colsSpec.axesSpec, func (a) { return a.name})
|
|
155
|
+
if !slices.hasElement(axesNames, datasetSpec.axesSpec[1].name) {
|
|
156
|
+
for na, ax in colsSpec.axesSpec {
|
|
157
|
+
if ax.name != datasetSpec.axesSpec[1].name {
|
|
158
|
+
cloneTable.setAxisHeader(ax.name, "cluster_" + string(i) + string(na))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Get linker columns if needed
|
|
166
|
+
linkerAxisSpec := {}
|
|
167
|
+
if len(columns.getColumns("linkers")) > 0 {
|
|
168
|
+
for i, col in columns.getColumns("linkers") {
|
|
169
|
+
if datasetSpec.axesSpec[1].name == col.spec.axesSpec[1].name {
|
|
170
|
+
cloneTable.add(col, {header: "linker." + string(i)})
|
|
171
|
+
cloneTable.setAxisHeader(col.spec.axesSpec[0].name, "cluster_" + string(i))
|
|
172
|
+
linkerAxisSpec["cluster_" + string(i)] = col.spec.axesSpec[0]
|
|
173
|
+
} else if datasetSpec.axesSpec[1].name == col.spec.axesSpec[0].name {
|
|
174
|
+
cloneTable.add(col, {header: "linker." + string(i)})
|
|
175
|
+
cloneTable.setAxisHeader(col.spec.axesSpec[1].name, "cluster_" + string(i))
|
|
176
|
+
linkerAxisSpec["cluster_" + string(i)] = col.spec.axesSpec[1]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
cloneTable.mem("16GiB")
|
|
181
|
+
cloneTable.cpu(1)
|
|
182
|
+
cloneTable = cloneTable.build()
|
|
183
|
+
|
|
184
|
+
// Use ender.create to call the filter-clonotypes template
|
|
185
|
+
filterResult := render.create(filterAndSampleTpl, {
|
|
186
|
+
inputAnchor: args.inputAnchor,
|
|
187
|
+
cloneTable: cloneTable,
|
|
188
|
+
topClonotypes: args.topClonotypes,
|
|
189
|
+
rankingOrder: args.rankingOrder,
|
|
190
|
+
rankingOrderDefault: args.rankingOrderDefault,
|
|
191
|
+
filters: args.filters,
|
|
192
|
+
filterMap: filterMap,
|
|
193
|
+
datasetSpec: datasetSpec
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Get the filtered clonotypes from the template result
|
|
197
|
+
outputs["sampledRows"] = filterResult.output("sampledRows", 24 * 60 * 60 * 1000)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
outputs: outputs,
|
|
202
|
+
exports: {}
|
|
203
|
+
}
|
|
204
|
+
})
|