@sjcrh/proteinpaint-server 2.59.0 → 2.61.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/package.json +2 -2
- package/routes/dzimages.js +64 -0
- package/routes/sampledzimages.js +50 -0
- package/routes/termdb.DE.js +129 -0
- package/routes/termdb.cluster.js +35 -27
- package/routes/termdb.config.js +15 -1
- package/routes/termdb.getTopTermsByType.js +106 -0
- package/routes/termdb.getdescrstats.js +1 -1
- package/src/app.js +1878 -818
- package/utils/edge.R +35 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.61.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "a genomics visualization tool for exploring a cohort's genotype and phenotype data",
|
|
6
6
|
"main": "src/app.js",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@sjcrh/augen": "2.46.0",
|
|
64
|
-
"@sjcrh/proteinpaint-rust": "2.
|
|
64
|
+
"@sjcrh/proteinpaint-rust": "2.61.1",
|
|
65
65
|
"better-sqlite3": "^9.4.1",
|
|
66
66
|
"body-parser": "^1.15.2",
|
|
67
67
|
"canvas": "~2.11.2",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import serverconfig from "#src/serverconfig.js";
|
|
3
|
+
import { illegalpath } from "#src/utils.js";
|
|
4
|
+
const routePath = "dzimages";
|
|
5
|
+
const api = {
|
|
6
|
+
endpoint: `${routePath}/:sampleId`,
|
|
7
|
+
methods: {
|
|
8
|
+
get: {
|
|
9
|
+
init,
|
|
10
|
+
request: {
|
|
11
|
+
typeId: "any"
|
|
12
|
+
},
|
|
13
|
+
response: {
|
|
14
|
+
typeId: "any"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
post: {
|
|
18
|
+
alternativeFor: "get",
|
|
19
|
+
init
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function init({ genomes }) {
|
|
24
|
+
return async (req, res) => {
|
|
25
|
+
let imagePath;
|
|
26
|
+
try {
|
|
27
|
+
const g = genomes[req.query.genome];
|
|
28
|
+
if (!g)
|
|
29
|
+
throw "invalid genome name";
|
|
30
|
+
const ds = g.datasets[req.query.dslabel];
|
|
31
|
+
if (!ds)
|
|
32
|
+
throw "invalid dataset name";
|
|
33
|
+
const sampleId = req.params.sampleId;
|
|
34
|
+
if (!sampleId)
|
|
35
|
+
throw "invalid sampleId";
|
|
36
|
+
if (illegalpath(req.query.file))
|
|
37
|
+
throw `illegalpath filepath`;
|
|
38
|
+
const filename = path.basename(req.query.file);
|
|
39
|
+
const allowedExtensions = [".dzi", ".jpeg", ".png"];
|
|
40
|
+
const extension = path.extname(filename);
|
|
41
|
+
if (!allowedExtensions.includes(extension)) {
|
|
42
|
+
throw `Invalid file extension. Allowed extensions are ${allowedExtensions.join(", ")}`;
|
|
43
|
+
}
|
|
44
|
+
imagePath = path.join(
|
|
45
|
+
`${serverconfig.tpmasterdir}/${ds.queries.DZImages.imageBySampleFolder}`,
|
|
46
|
+
`${sampleId}/${req.query.file}`
|
|
47
|
+
);
|
|
48
|
+
res.sendFile(imagePath, (err) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
res.status(404).send("Image not found");
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.log(e);
|
|
55
|
+
res.send({
|
|
56
|
+
status: "error",
|
|
57
|
+
error: e.error || e
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
api
|
|
64
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import serverconfig from "#src/serverconfig.js";
|
|
4
|
+
const api = {
|
|
5
|
+
endpoint: "sampledzimages",
|
|
6
|
+
methods: {
|
|
7
|
+
get: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: "GetSampleDZImagesRequest"
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: "GetSampleDZImagesResponse"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
post: {
|
|
17
|
+
alternativeFor: "get",
|
|
18
|
+
init
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function init({ genomes }) {
|
|
23
|
+
return async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const g = genomes[req.query.genome];
|
|
26
|
+
if (!g)
|
|
27
|
+
throw "invalid genome name";
|
|
28
|
+
const ds = g.datasets[req.query.dslabel];
|
|
29
|
+
if (!ds)
|
|
30
|
+
throw "invalid dataset name";
|
|
31
|
+
const sampleId = req.query.sample_id;
|
|
32
|
+
const sampleDZImagesPath = path.join(
|
|
33
|
+
`${serverconfig.tpmasterdir}/${ds.queries.DZImages.imageBySampleFolder}`,
|
|
34
|
+
sampleId
|
|
35
|
+
);
|
|
36
|
+
const sampleDZImages = getDZImages(sampleDZImagesPath);
|
|
37
|
+
res.send({ sampleDZImages });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.log(e);
|
|
40
|
+
res.status(404).send("Sample images not found");
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function getDZImages(sampleImagesPath) {
|
|
45
|
+
const files = fs.readdirSync(sampleImagesPath);
|
|
46
|
+
return files.filter((file) => path.extname(file) === ".dzi");
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
api
|
|
50
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { run_rust } from "@sjcrh/proteinpaint-rust";
|
|
3
|
+
import { get_ds_tdb } from "../src/termdb.js";
|
|
4
|
+
import run_R from "../src/run_R.js";
|
|
5
|
+
import serverconfig from "../src/serverconfig.js";
|
|
6
|
+
const api = {
|
|
7
|
+
endpoint: "DEanalysis",
|
|
8
|
+
methods: {
|
|
9
|
+
all: {
|
|
10
|
+
init,
|
|
11
|
+
request: {
|
|
12
|
+
typeId: "DERequest"
|
|
13
|
+
},
|
|
14
|
+
response: {
|
|
15
|
+
typeId: "DEResponse"
|
|
16
|
+
// will combine this with type checker
|
|
17
|
+
//valid: (t) => {}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function init({ genomes }) {
|
|
23
|
+
return async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const q = req.query;
|
|
26
|
+
const genome = genomes[q.genome];
|
|
27
|
+
if (!genome)
|
|
28
|
+
throw "invalid genome";
|
|
29
|
+
const [ds] = get_ds_tdb(genome, q);
|
|
30
|
+
const results = await run_DE(req.query, ds);
|
|
31
|
+
res.send(results);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
res.send({ status: "error", error: e.message || e });
|
|
34
|
+
if (e instanceof Error && e.stack)
|
|
35
|
+
console.log(e);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function run_DE(param, ds) {
|
|
40
|
+
if (param.samplelst?.groups?.length != 2)
|
|
41
|
+
throw ".samplelst.groups.length!=2";
|
|
42
|
+
if (param.samplelst.groups[0].values?.length < 1)
|
|
43
|
+
throw "samplelst.groups[0].values.length<1";
|
|
44
|
+
if (param.samplelst.groups[1].values?.length < 1)
|
|
45
|
+
throw "samplelst.groups[1].values.length<1";
|
|
46
|
+
const q = ds.queries.rnaseqGeneCount;
|
|
47
|
+
if (!q)
|
|
48
|
+
return;
|
|
49
|
+
if (!q.file)
|
|
50
|
+
throw "unknown data type for rnaseqGeneCount";
|
|
51
|
+
const group1names = [];
|
|
52
|
+
for (const s of param.samplelst.groups[0].values) {
|
|
53
|
+
if (!Number.isInteger(s.sampleId))
|
|
54
|
+
continue;
|
|
55
|
+
const n = ds.cohort.termdb.q.id2sampleName(s.sampleId);
|
|
56
|
+
if (!n)
|
|
57
|
+
continue;
|
|
58
|
+
if (q.allSampleSet.has(n)) {
|
|
59
|
+
group1names.push(n);
|
|
60
|
+
} else {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const group2names = [];
|
|
64
|
+
for (const s of param.samplelst.groups[1].values) {
|
|
65
|
+
if (!Number.isInteger(s.sampleId))
|
|
66
|
+
continue;
|
|
67
|
+
const n = ds.cohort.termdb.q.id2sampleName(s.sampleId);
|
|
68
|
+
if (!n)
|
|
69
|
+
continue;
|
|
70
|
+
if (q.allSampleSet.has(n)) {
|
|
71
|
+
group2names.push(n);
|
|
72
|
+
} else {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const sample_size1 = group1names.length;
|
|
76
|
+
const sample_size2 = group2names.length;
|
|
77
|
+
if (sample_size1 < 1)
|
|
78
|
+
throw "sample size of group1 < 1";
|
|
79
|
+
if (sample_size2 < 1)
|
|
80
|
+
throw "sample size of group2 < 1";
|
|
81
|
+
const cases_string = group1names.map((i) => i).join(",");
|
|
82
|
+
const controls_string = group2names.map((i) => i).join(",");
|
|
83
|
+
const expression_input = {
|
|
84
|
+
case: cases_string,
|
|
85
|
+
control: controls_string,
|
|
86
|
+
input_file: q.file,
|
|
87
|
+
min_count: param.min_count,
|
|
88
|
+
min_total_count: param.min_total_count
|
|
89
|
+
};
|
|
90
|
+
const sample_size_limit = 8;
|
|
91
|
+
let result;
|
|
92
|
+
if (group1names.length <= sample_size_limit && group2names.length <= sample_size_limit || param.method == "edgeR") {
|
|
93
|
+
const time1 = (/* @__PURE__ */ new Date()).valueOf();
|
|
94
|
+
result = JSON.parse(
|
|
95
|
+
await run_R(path.join(serverconfig.binpath, "utils", "edge.R"), JSON.stringify(expression_input))
|
|
96
|
+
);
|
|
97
|
+
const time2 = (/* @__PURE__ */ new Date()).valueOf();
|
|
98
|
+
console.log("Time taken to run edgeR:", time2 - time1, "ms");
|
|
99
|
+
param.method = "edgeR";
|
|
100
|
+
} else if (param.method == "wilcoxon") {
|
|
101
|
+
const time1 = (/* @__PURE__ */ new Date()).valueOf();
|
|
102
|
+
const rust_output = await run_rust("DEanalysis", JSON.stringify(expression_input));
|
|
103
|
+
const time2 = (/* @__PURE__ */ new Date()).valueOf();
|
|
104
|
+
for (const line of rust_output.split("\n")) {
|
|
105
|
+
if (line.startsWith("adjusted_p_values:")) {
|
|
106
|
+
result = JSON.parse(line.replace("adjusted_p_values:", ""));
|
|
107
|
+
} else {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
console.log("Time taken to run rust DE pipeline:", time2 - time1, "ms");
|
|
111
|
+
param.method = "wilcoxon";
|
|
112
|
+
} else {
|
|
113
|
+
const time1 = (/* @__PURE__ */ new Date()).valueOf();
|
|
114
|
+
const rust_output = await run_rust("DEanalysis", JSON.stringify(expression_input));
|
|
115
|
+
const time2 = (/* @__PURE__ */ new Date()).valueOf();
|
|
116
|
+
for (const line of rust_output.split("\n")) {
|
|
117
|
+
if (line.startsWith("adjusted_p_values:")) {
|
|
118
|
+
result = JSON.parse(line.replace("adjusted_p_values:", ""));
|
|
119
|
+
} else {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
console.log("Time taken to run rust DE pipeline:", time2 - time1, "ms");
|
|
123
|
+
param.method = "wilcoxon";
|
|
124
|
+
}
|
|
125
|
+
return { data: result, sample_size1, sample_size2, method: param.method };
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
api
|
|
129
|
+
};
|
package/routes/termdb.cluster.js
CHANGED
|
@@ -4,9 +4,9 @@ import * as utils from "#src/utils.js";
|
|
|
4
4
|
import serverconfig from "#src/serverconfig.js";
|
|
5
5
|
import { gdc_validate_query_geneExpression } from "#src/mds3.gdc.js";
|
|
6
6
|
import { mayLimitSamples } from "#src/mds3.filter.js";
|
|
7
|
-
import { dtgeneexpression } from "#shared/common.js";
|
|
8
7
|
import { clusterMethodLst, distanceMethodLst } from "#shared/clustering.js";
|
|
9
8
|
import { getResult as getResultGene } from "#src/gene.js";
|
|
9
|
+
import { TermTypes } from "#shared/terms.js";
|
|
10
10
|
const api = {
|
|
11
11
|
endpoint: "termdb/cluster",
|
|
12
12
|
methods: {
|
|
@@ -34,9 +34,9 @@ function init({ genomes }) {
|
|
|
34
34
|
throw "invalid dataset name";
|
|
35
35
|
if (ds.__gdc && !ds.__gdc.doneCaching)
|
|
36
36
|
throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
|
|
37
|
-
if (q.dataType ==
|
|
38
|
-
if (!ds.queries?.
|
|
39
|
-
throw
|
|
37
|
+
if (q.dataType == TermTypes.GENE_EXPRESSION || q.dataType == TermTypes.METABOLITE_INTENSITY) {
|
|
38
|
+
if (!ds.queries?.[q.dataType])
|
|
39
|
+
throw `no ${q.dataType} data on this dataset`;
|
|
40
40
|
result = await getResult(q, ds);
|
|
41
41
|
} else {
|
|
42
42
|
throw "unknown q.dataType " + q.dataType;
|
|
@@ -53,15 +53,16 @@ function init({ genomes }) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
async function getResult(q, ds) {
|
|
56
|
-
const
|
|
57
|
-
|
|
56
|
+
const type = q.dataType;
|
|
57
|
+
const { term2sample2value, byTermId, bySampleId } = await ds.queries[type].get(q);
|
|
58
|
+
if (term2sample2value.size == 0)
|
|
58
59
|
throw "no data";
|
|
59
|
-
if (
|
|
60
|
-
const g = Array.from(
|
|
61
|
-
return { gene: g, data:
|
|
60
|
+
if (term2sample2value.size == 1) {
|
|
61
|
+
const g = Array.from(term2sample2value.keys())[0];
|
|
62
|
+
return { term: { gene: g, type: TermTypes.GENE_EXPRESSION }, data: term2sample2value.get(g) };
|
|
62
63
|
}
|
|
63
64
|
const t = Date.now();
|
|
64
|
-
const clustering = await doClustering(
|
|
65
|
+
const clustering = await doClustering(term2sample2value, q);
|
|
65
66
|
if (serverconfig.debugmode)
|
|
66
67
|
console.log("clustering done:", Date.now() - t, "ms");
|
|
67
68
|
return { clustering, byTermId, bySampleId };
|
|
@@ -137,7 +138,7 @@ async function validate_query_geneExpression(ds, genome) {
|
|
|
137
138
|
const q = ds.queries.geneExpression;
|
|
138
139
|
if (!q)
|
|
139
140
|
return;
|
|
140
|
-
q.
|
|
141
|
+
q.geneExpression2bins = {};
|
|
141
142
|
if (q.src == "gdcapi") {
|
|
142
143
|
gdc_validate_query_geneExpression(ds, genome);
|
|
143
144
|
return;
|
|
@@ -169,12 +170,11 @@ async function validateNative(q, ds, genome) {
|
|
|
169
170
|
throw "queries.geneExpression: unknown sample from header: " + l[i];
|
|
170
171
|
q.samples.push(id);
|
|
171
172
|
}
|
|
172
|
-
console.log(q.samples.length, "samples from geneExpression of", ds.label);
|
|
173
173
|
}
|
|
174
174
|
q.get = async (param) => {
|
|
175
175
|
const limitSamples = await mayLimitSamples(param, q.samples, ds);
|
|
176
176
|
if (limitSamples?.size == 0) {
|
|
177
|
-
return {
|
|
177
|
+
return { term2sample2value: /* @__PURE__ */ new Set(), byTermId: {}, bySampleId: {} };
|
|
178
178
|
}
|
|
179
179
|
const bySampleId = {};
|
|
180
180
|
const samples = q.samples || [];
|
|
@@ -187,26 +187,32 @@ async function validateNative(q, ds, genome) {
|
|
|
187
187
|
bySampleId[sid] = { label: ds.cohort.termdb.q.id2sampleName(sid) };
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
const
|
|
191
|
-
for (const g of param.
|
|
192
|
-
|
|
190
|
+
const term2sample2value = /* @__PURE__ */ new Map();
|
|
191
|
+
for (const g of param.terms) {
|
|
192
|
+
const geneTerm = g;
|
|
193
|
+
if (!geneTerm.gene)
|
|
193
194
|
continue;
|
|
194
|
-
if (!
|
|
195
|
-
const re = getResultGene(genome, { input:
|
|
196
|
-
if (!re.gmlst || re.gmlst.length == 0)
|
|
197
|
-
|
|
195
|
+
if (!geneTerm.chr) {
|
|
196
|
+
const re = getResultGene(genome, { input: geneTerm.gene, deep: 1 });
|
|
197
|
+
if (!re.gmlst || re.gmlst.length == 0) {
|
|
198
|
+
console.warn("unknown gene:" + geneTerm.gene);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
198
201
|
const i = re.gmlst.find((i2) => i2.isdefault) || re.gmlst[0];
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
geneTerm.start = i.start;
|
|
203
|
+
geneTerm.stop = i.stop;
|
|
204
|
+
geneTerm.chr = i.chr;
|
|
202
205
|
}
|
|
203
206
|
const s2v = {};
|
|
204
207
|
await utils.get_lines_bigfile({
|
|
205
|
-
args: [
|
|
208
|
+
args: [
|
|
209
|
+
q.file,
|
|
210
|
+
(q.nochr ? geneTerm.chr?.replace("chr", "") : geneTerm.chr) + ":" + geneTerm.start + "-" + geneTerm.stop
|
|
211
|
+
],
|
|
206
212
|
// must do g.chr?.replace to avoid tsc error
|
|
207
213
|
callback: (line) => {
|
|
208
214
|
const l = line.split(" ");
|
|
209
|
-
if (l[3].toLowerCase() !=
|
|
215
|
+
if (l[3].toLowerCase() != geneTerm.gene.toLowerCase())
|
|
210
216
|
return;
|
|
211
217
|
for (let i = 4; i < l.length; i++) {
|
|
212
218
|
const sampleId = samples[i - 4];
|
|
@@ -222,10 +228,12 @@ async function validateNative(q, ds, genome) {
|
|
|
222
228
|
}
|
|
223
229
|
});
|
|
224
230
|
if (Object.keys(s2v).length)
|
|
225
|
-
|
|
231
|
+
term2sample2value.set(geneTerm.gene, s2v);
|
|
226
232
|
}
|
|
227
233
|
const byTermId = {};
|
|
228
|
-
|
|
234
|
+
if (term2sample2value.size == 0)
|
|
235
|
+
throw "no data available for the input " + param.terms?.map((g) => g.gene).join(", ");
|
|
236
|
+
return { term2sample2value, byTermId, bySampleId };
|
|
229
237
|
};
|
|
230
238
|
}
|
|
231
239
|
export {
|
package/routes/termdb.config.js
CHANGED
|
@@ -3,6 +3,7 @@ import { authApi } from "#src/auth.js";
|
|
|
3
3
|
import { get_ds_tdb } from "#src/termdb.js";
|
|
4
4
|
import { mayCopyFromCookie } from "#src/utils.js";
|
|
5
5
|
import { mayComputeTermtypeByCohort } from "#src/termdb.server.init.js";
|
|
6
|
+
import { TermTypes } from "#shared/terms.js";
|
|
6
7
|
const api = {
|
|
7
8
|
endpoint: "termdb/config",
|
|
8
9
|
methods: {
|
|
@@ -168,6 +169,17 @@ function addGenomicQueries(c, ds, genome) {
|
|
|
168
169
|
delete q2.singleSampleGenomeQuantification[k].folder;
|
|
169
170
|
}
|
|
170
171
|
}
|
|
172
|
+
if (q.NIdata) {
|
|
173
|
+
q2.NIdata = {};
|
|
174
|
+
for (const k in q.NIdata) {
|
|
175
|
+
q2.NIdata[k] = JSON.parse(JSON.stringify(q.NIdata[k]));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (q.DZImages) {
|
|
179
|
+
q2.DZImages = {
|
|
180
|
+
type: q.DZImages.type
|
|
181
|
+
};
|
|
182
|
+
}
|
|
171
183
|
if (q.singleSampleGbtk) {
|
|
172
184
|
q2.singleSampleGbtk = {};
|
|
173
185
|
for (const k in q.singleSampleGbtk) {
|
|
@@ -207,7 +219,9 @@ function getAllowedTermTypes(ds) {
|
|
|
207
219
|
typeSet.add("geneVariant");
|
|
208
220
|
}
|
|
209
221
|
if (ds?.queries?.geneExpression)
|
|
210
|
-
typeSet.add(
|
|
222
|
+
typeSet.add(TermTypes.GENE_EXPRESSION);
|
|
223
|
+
if (ds?.queries?.metaboliteIntensity)
|
|
224
|
+
typeSet.add(TermTypes.METABOLITE_INTENSITY);
|
|
211
225
|
return [...typeSet];
|
|
212
226
|
}
|
|
213
227
|
export {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { run_rust } from "@sjcrh/proteinpaint-rust";
|
|
2
|
+
import { get_samples } from "#src/termdb.sql.js";
|
|
3
|
+
import { TermTypes } from "#shared/terms.js";
|
|
4
|
+
const api = {
|
|
5
|
+
endpoint: "termdb/getTopTermsByType",
|
|
6
|
+
methods: {
|
|
7
|
+
all: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: "TermdbTopTermsByTypeRequest"
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: "TermdbTopTermsByTypeResponse"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
function init({ genomes }) {
|
|
19
|
+
return async (req, res) => {
|
|
20
|
+
try {
|
|
21
|
+
const q = req.query;
|
|
22
|
+
const type = q.type;
|
|
23
|
+
const genome = genomes[q.genome];
|
|
24
|
+
if (!genome)
|
|
25
|
+
throw "invalid genome";
|
|
26
|
+
const ds = genome.datasets?.[q.dslabel];
|
|
27
|
+
if (!ds)
|
|
28
|
+
throw "invalid dslabel";
|
|
29
|
+
if (!ds.queries[type])
|
|
30
|
+
throw "not supported on dataset";
|
|
31
|
+
const t = Date.now();
|
|
32
|
+
const terms = await ds.queries[type].getTopTerms(q);
|
|
33
|
+
res.send({ terms });
|
|
34
|
+
} catch (e) {
|
|
35
|
+
res.send({ status: "error", error: e.message || e });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function validate_query_getTopTermsByType(ds, genome) {
|
|
40
|
+
const types = [TermTypes.METABOLITE_INTENSITY];
|
|
41
|
+
for (const type of types) {
|
|
42
|
+
if (ds.queries[type]) {
|
|
43
|
+
const q = ds.queries[type];
|
|
44
|
+
if (!q)
|
|
45
|
+
return;
|
|
46
|
+
if (q.src == "gdcapi")
|
|
47
|
+
gdcValidateQuery(ds, genome, type);
|
|
48
|
+
else if (q.src == "native")
|
|
49
|
+
nativeValidateQuery(ds, type);
|
|
50
|
+
else
|
|
51
|
+
throw "unknown topVariablyExpressedGenes.src";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function nativeValidateQuery(ds, type) {
|
|
56
|
+
ds.queries[type].getTopTerms = async (q) => {
|
|
57
|
+
const typeQuery = ds.queries[type];
|
|
58
|
+
const samples = [];
|
|
59
|
+
if (q.filter) {
|
|
60
|
+
const sidlst = await get_samples(q.filter, ds);
|
|
61
|
+
for (const i of sidlst) {
|
|
62
|
+
if (typeQuery.samples.includes(i.id)) {
|
|
63
|
+
const n = ds.cohort.termdb.q.id2sampleName(i.id);
|
|
64
|
+
if (!n)
|
|
65
|
+
throw "sample id cannot convert to string name";
|
|
66
|
+
samples.push(n);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
for (const i of typeQuery.samples) {
|
|
71
|
+
const n = ds.cohort.termdb.q.id2sampleName(i.id);
|
|
72
|
+
if (!n)
|
|
73
|
+
throw "sample id cannot convert to string name";
|
|
74
|
+
samples.push(n);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const terms = await computeTopTerms(typeQuery.file, samples, type);
|
|
78
|
+
return terms;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function computeTopTerms(file, samples, type) {
|
|
82
|
+
const input_json = {
|
|
83
|
+
input_file: file,
|
|
84
|
+
samples: samples.join(","),
|
|
85
|
+
param: "var"
|
|
86
|
+
};
|
|
87
|
+
const rust_result = await run_rust("computeTopTerms", JSON.stringify(input_json));
|
|
88
|
+
const rust_result_list = rust_result.split("\n");
|
|
89
|
+
let output_json;
|
|
90
|
+
for (const item of rust_result_list) {
|
|
91
|
+
if (item.includes("output_json")) {
|
|
92
|
+
output_json = JSON.parse(item.replace("output_json:", ""));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const varMetabolite = output_json.map((i) => ({ name: i.metabolite, type }));
|
|
96
|
+
return varMetabolite;
|
|
97
|
+
}
|
|
98
|
+
function gdcValidateQuery(ds, genome, type) {
|
|
99
|
+
ds.queries[type].getTopTerms = async (q) => {
|
|
100
|
+
return [];
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
api,
|
|
105
|
+
validate_query_getTopTermsByType
|
|
106
|
+
};
|
|
@@ -81,7 +81,7 @@ async function trigger_getdescrstats(q, res, ds, genome) {
|
|
|
81
81
|
for (const key in data.samples) {
|
|
82
82
|
const sample = data.samples[key];
|
|
83
83
|
const value = sample[q.tw.$id].value;
|
|
84
|
-
if (q.tw.
|
|
84
|
+
if (q.tw.q.hiddenValues?.[value]) {
|
|
85
85
|
continue;
|
|
86
86
|
}
|
|
87
87
|
if (q.settings?.violin?.unit === "log") {
|