@sjcrh/proteinpaint-server 2.63.6 → 2.65.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.
- package/package.json +1 -1
- package/routes/genesetEnrichment.js +107 -0
- package/routes/hicdata.js +17 -16
- package/routes/{hicdata.genome.js → hicgenome.js} +22 -15
- package/routes/termdb.config.js +1 -1
- package/routes/termdb.singlecellSamples.js +23 -7
- package/src/app.js +899 -600
- package/src/serverconfig.js +1 -0
- package/utils/gsea.py +75 -0
package/package.json
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { Readable } from "stream";
|
|
4
|
+
import serverconfig from "../src/serverconfig.js";
|
|
5
|
+
const api = {
|
|
6
|
+
endpoint: "genesetEnrichment",
|
|
7
|
+
methods: {
|
|
8
|
+
all: {
|
|
9
|
+
init,
|
|
10
|
+
request: {
|
|
11
|
+
typeId: "genesetEnrichmentRequest"
|
|
12
|
+
},
|
|
13
|
+
response: {
|
|
14
|
+
typeId: "genesetEnrichmentResponse"
|
|
15
|
+
// will combine this with type checker
|
|
16
|
+
//valid: (t) => {}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function init({ genomes }) {
|
|
22
|
+
return async (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const results = await run_genesetEnrichment_analysis(req.query, genomes);
|
|
25
|
+
res.send(results);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
res.send({ status: "error", error: e.message || e });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function run_genesetEnrichment_analysis(q, genomes) {
|
|
32
|
+
if (!genomes[q.genome].termdbs)
|
|
33
|
+
throw "termdb database is not available for " + q.genome;
|
|
34
|
+
const genesetenrichment_input = {
|
|
35
|
+
genes: q.genes,
|
|
36
|
+
fold_change: q.fold_change,
|
|
37
|
+
db: genomes[q.genome].termdbs.msigdb.cohort.db.connection.name,
|
|
38
|
+
// For now msigdb has been added, but later databases other than msigdb may be used
|
|
39
|
+
gene_set_group: q.geneSetGroup
|
|
40
|
+
};
|
|
41
|
+
const gsea_output = await run_gsea(
|
|
42
|
+
`${serverconfig.binpath}/utils/gsea.py`,
|
|
43
|
+
"/" + JSON.stringify(genesetenrichment_input)
|
|
44
|
+
// "/" is needed for python to accept the bracket "{" as a bracket
|
|
45
|
+
);
|
|
46
|
+
let result;
|
|
47
|
+
for (const line of gsea_output.split("\n")) {
|
|
48
|
+
if (line.startsWith("result: ")) {
|
|
49
|
+
result = JSON.parse(line.replace("result: ", ""));
|
|
50
|
+
} else {
|
|
51
|
+
console.log(line);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
async function run_gsea(path2, data) {
|
|
57
|
+
try {
|
|
58
|
+
await fs.promises.stat(path2);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
throw `${path2} does not exist`;
|
|
61
|
+
}
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const _stdout = [];
|
|
64
|
+
const _stderr = [];
|
|
65
|
+
const sp = spawn(serverconfig.python, [path2]);
|
|
66
|
+
if (data) {
|
|
67
|
+
try {
|
|
68
|
+
const input = data.endsWith("\n") ? data : data + "\n";
|
|
69
|
+
Readable.from(input).pipe(sp.stdin);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
sp.kill();
|
|
72
|
+
let errmsg = e;
|
|
73
|
+
const stderr = _stderr.join("").trim();
|
|
74
|
+
if (stderr)
|
|
75
|
+
errmsg += `
|
|
76
|
+
python stderr: ${stderr}`;
|
|
77
|
+
reject(errmsg);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
sp.stdout.on("data", (data2) => _stdout.push(data2));
|
|
81
|
+
sp.stderr.on("data", (data2) => _stderr.push(data2));
|
|
82
|
+
sp.on("error", (err) => reject(err));
|
|
83
|
+
sp.on("close", (code) => {
|
|
84
|
+
const stdout = _stdout.join("").trim();
|
|
85
|
+
const stderr = _stderr.join("").trim();
|
|
86
|
+
if (code !== 0) {
|
|
87
|
+
let errmsg = `python process exited with non-zero status code=${code}`;
|
|
88
|
+
if (stdout)
|
|
89
|
+
errmsg += `
|
|
90
|
+
python stdout: ${stdout}`;
|
|
91
|
+
if (stderr)
|
|
92
|
+
errmsg += `
|
|
93
|
+
python stderr: ${stderr}`;
|
|
94
|
+
reject(errmsg);
|
|
95
|
+
}
|
|
96
|
+
if (stderr) {
|
|
97
|
+
const errmsg = `python process emitted standard error
|
|
98
|
+
python stderr: ${stderr}`;
|
|
99
|
+
reject(errmsg);
|
|
100
|
+
}
|
|
101
|
+
resolve(stdout);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
api
|
|
107
|
+
};
|
package/routes/hicdata.js
CHANGED
|
@@ -12,24 +12,25 @@ const api = {
|
|
|
12
12
|
},
|
|
13
13
|
response: {
|
|
14
14
|
typeId: "HicdataResponse"
|
|
15
|
-
}
|
|
16
|
-
/*
|
|
15
|
+
},
|
|
17
16
|
examples: [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
{
|
|
18
|
+
request: {
|
|
19
|
+
body: {
|
|
20
|
+
embedder: "localhost",
|
|
21
|
+
url: "https://proteinpaint.stjude.org/ppdemo/hg19/hic/hic_demo.hic",
|
|
22
|
+
matrixType: "observed",
|
|
23
|
+
nmeth: "NONE",
|
|
24
|
+
pos1: "3",
|
|
25
|
+
pos2: "2",
|
|
26
|
+
resolution: 1e6
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
response: {
|
|
30
|
+
header: { status: 200 }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
31
33
|
]
|
|
32
|
-
*/
|
|
33
34
|
},
|
|
34
35
|
post: {
|
|
35
36
|
alternativeFor: "get",
|
|
@@ -8,11 +8,29 @@ const api = {
|
|
|
8
8
|
get: {
|
|
9
9
|
init,
|
|
10
10
|
request: {
|
|
11
|
-
typeId: "
|
|
11
|
+
typeId: "HicGenomeRequest"
|
|
12
12
|
},
|
|
13
13
|
response: {
|
|
14
|
-
typeId: "
|
|
15
|
-
}
|
|
14
|
+
typeId: "HicGenomeResponse"
|
|
15
|
+
},
|
|
16
|
+
examples: [
|
|
17
|
+
{
|
|
18
|
+
request: {
|
|
19
|
+
body: {
|
|
20
|
+
chrlst: ["chr1", "chr2"],
|
|
21
|
+
embedder: "localhost",
|
|
22
|
+
url: "https://proteinpaint.stjude.org/ppdemo/hg19/hic/hic_demo.hic",
|
|
23
|
+
matrixType: "observed",
|
|
24
|
+
nmeth: "NONE",
|
|
25
|
+
nochr: true,
|
|
26
|
+
resolution: 25e5
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
response: {
|
|
30
|
+
header: { status: 200 }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]
|
|
16
34
|
},
|
|
17
35
|
post: {
|
|
18
36
|
alternativeFor: "get",
|
|
@@ -34,15 +52,7 @@ function init() {
|
|
|
34
52
|
return new Promise((resolve, reject) => {
|
|
35
53
|
const pos1 = req.query.nochr ? lead.replace("chr", "") : lead;
|
|
36
54
|
const pos2 = req.query.nochr ? follow.replace("chr", "") : follow;
|
|
37
|
-
const par = [
|
|
38
|
-
matrixType,
|
|
39
|
-
req.query.nmeth || "NONE",
|
|
40
|
-
file,
|
|
41
|
-
pos1,
|
|
42
|
-
pos2,
|
|
43
|
-
req.query.isfrag ? "FRAG" : "BP",
|
|
44
|
-
req.query.resolution
|
|
45
|
-
];
|
|
55
|
+
const par = [matrixType, req.query.nmeth || "NONE", file, pos1, pos2, "BP", req.query.resolution];
|
|
46
56
|
const ps = spawn(serverconfig.hicstraw, par);
|
|
47
57
|
const rl = readline.createInterface({ input: ps.stdout });
|
|
48
58
|
const items = [];
|
|
@@ -61,9 +71,6 @@ function init() {
|
|
|
61
71
|
fieldnotnumerical++;
|
|
62
72
|
return;
|
|
63
73
|
}
|
|
64
|
-
if (req.query.mincutoff != void 0 && v <= req.query.mincutoff) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
74
|
items.push([n1, n2, v]);
|
|
68
75
|
});
|
|
69
76
|
data.push({ items, lead, follow });
|
package/routes/termdb.config.js
CHANGED
|
@@ -42,7 +42,7 @@ function make(q, res, ds, genome) {
|
|
|
42
42
|
const c = {
|
|
43
43
|
selectCohort: tdb.selectCohort,
|
|
44
44
|
// optional
|
|
45
|
-
supportedChartTypes: tdb.q
|
|
45
|
+
supportedChartTypes: tdb.q?.getSupportedChartTypes(q.embedder),
|
|
46
46
|
hiddenChartTypes: ds.cohort.hiddenChartTypes,
|
|
47
47
|
renamedChartTypes: ds.cohort.renamedChartTypes,
|
|
48
48
|
allowedTermTypes: getAllowedTermTypes(ds),
|
|
@@ -61,7 +61,7 @@ async function validate_query_singleCell(ds, genome) {
|
|
|
61
61
|
if (q.data.src == "gdcapi") {
|
|
62
62
|
gdc_validate_query_singleCell_data(ds, genome);
|
|
63
63
|
} else if (q.data.src == "native") {
|
|
64
|
-
validateDataNative(q.data);
|
|
64
|
+
validateDataNative(q.data, ds);
|
|
65
65
|
} else {
|
|
66
66
|
throw "unknown singleCell.data.src";
|
|
67
67
|
}
|
|
@@ -90,7 +90,7 @@ async function validateSamplesNative(S, ds) {
|
|
|
90
90
|
return { samples: Object.values(samples) };
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
function validateDataNative(D) {
|
|
93
|
+
function validateDataNative(D, ds) {
|
|
94
94
|
const nameSet = /* @__PURE__ */ new Set();
|
|
95
95
|
for (const plot of D.plots) {
|
|
96
96
|
if (nameSet.has(plot.name))
|
|
@@ -100,6 +100,10 @@ function validateDataNative(D) {
|
|
|
100
100
|
D.get = async (q) => {
|
|
101
101
|
try {
|
|
102
102
|
const plots = [];
|
|
103
|
+
let geneExpMap;
|
|
104
|
+
if (ds.queries.singleCell.geneExpression && q.gene) {
|
|
105
|
+
geneExpMap = await ds.queries.singleCell.geneExpression.get({ sample: q.sample, gene: q.gene });
|
|
106
|
+
}
|
|
103
107
|
for (const plot of D.plots) {
|
|
104
108
|
const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, q.sample + plot.fileSuffix);
|
|
105
109
|
try {
|
|
@@ -116,13 +120,19 @@ function validateDataNative(D) {
|
|
|
116
120
|
const cells = [];
|
|
117
121
|
for (let i = 1; i < lines.length; i++) {
|
|
118
122
|
const l = lines[i].split(" ");
|
|
119
|
-
const cellId = l[0], x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
|
|
123
|
+
const cellId = lines.length > 3 ? l[0] : void 0, x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
|
|
120
124
|
const category = l[plot.colorColumn?.index] || "";
|
|
121
125
|
if (!cellId)
|
|
122
126
|
throw "cell id missing";
|
|
123
127
|
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
124
128
|
throw "x/y not number";
|
|
125
|
-
|
|
129
|
+
const cell = { cellId, x, y, category };
|
|
130
|
+
if (geneExpMap) {
|
|
131
|
+
if (geneExpMap[cellId] !== void 0) {
|
|
132
|
+
cell.geneExp = geneExpMap[cellId];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
cells.push(cell);
|
|
126
136
|
}
|
|
127
137
|
plots.push({ name: plot.name, cells, colorBy: plot.colorColumn?.name, colorMap: plot.colorColumn?.colorMap });
|
|
128
138
|
}
|
|
@@ -145,7 +155,9 @@ function validateGeneExpressionNative(G) {
|
|
|
145
155
|
} catch (e) {
|
|
146
156
|
throw "geneExp matrix file not found or readable for this sample";
|
|
147
157
|
}
|
|
148
|
-
const header = await get_header_txt(tsvfile);
|
|
158
|
+
const header = (await get_header_txt(tsvfile)).split(" ");
|
|
159
|
+
if (header.length == 0)
|
|
160
|
+
throw "blank header line";
|
|
149
161
|
return await grepMatrix4geneExpression(tsvfile, q.gene, header);
|
|
150
162
|
};
|
|
151
163
|
}
|
|
@@ -159,10 +171,14 @@ function grepMatrix4geneExpression(tsvfile, gene, header) {
|
|
|
159
171
|
const e = err.join("");
|
|
160
172
|
if (e)
|
|
161
173
|
reject(e);
|
|
162
|
-
const
|
|
174
|
+
const cell2value = {};
|
|
175
|
+
const line = out.join("").trim();
|
|
176
|
+
if (!line) {
|
|
177
|
+
resolve(cell2value);
|
|
178
|
+
}
|
|
179
|
+
const l = line.split(" ");
|
|
163
180
|
if (l.length != header.length)
|
|
164
181
|
reject(`number of fields differ between data line and header: ${l.length} ${header.length}`);
|
|
165
|
-
const cell2value = {};
|
|
166
182
|
for (let i = 1; i < l.length; i++) {
|
|
167
183
|
const v = Number(l[i]);
|
|
168
184
|
if (Number.isNaN(v))
|