@sjcrh/proteinpaint-server 2.69.0 → 2.69.1-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 +4 -1
- package/routes/genesetEnrichment.js +31 -3
- package/routes/sampletiaimages.js +51 -0
- package/routes/termdb.config.js +5 -0
- package/routes/tiaimages.js +83 -0
- package/routes/tileserver.js +40 -0
- package/src/app.js +806 -292
- package/utils/gsea.py +60 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.69.
|
|
3
|
+
"version": "2.69.1-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",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"@babel/preset-typescript": "^7.21.4",
|
|
45
45
|
"@babel/register": "^7.14.5",
|
|
46
46
|
"@types/node": "^20.11.24",
|
|
47
|
+
"@types/qs": "^6.9.15",
|
|
47
48
|
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
|
48
49
|
"babel-loader": "^8.2.2",
|
|
49
50
|
"esbuild": "^0.19.12",
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"dependencies": {
|
|
63
64
|
"@sjcrh/augen": "2.46.0",
|
|
64
65
|
"@sjcrh/proteinpaint-rust": "2.61.1",
|
|
66
|
+
"axios": "^1.7.2",
|
|
65
67
|
"better-sqlite3": "^9.4.1",
|
|
66
68
|
"body-parser": "^1.15.2",
|
|
67
69
|
"canvas": "~2.11.2",
|
|
@@ -81,6 +83,7 @@
|
|
|
81
83
|
"minimatch": "^3.1.2",
|
|
82
84
|
"node-fetch": "^2.6.1",
|
|
83
85
|
"partjson": "^0.58.2",
|
|
86
|
+
"qs": "^6.12.3",
|
|
84
87
|
"tiny-async-pool": "^1.2.0",
|
|
85
88
|
"typedoc-plugin-missing-exports": "^2.0.1"
|
|
86
89
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
2
3
|
import { spawn } from "child_process";
|
|
3
4
|
import { Readable } from "stream";
|
|
4
5
|
import serverconfig from "../src/serverconfig.js";
|
|
@@ -22,7 +23,20 @@ function init({ genomes }) {
|
|
|
22
23
|
return async (req, res) => {
|
|
23
24
|
try {
|
|
24
25
|
const results = await run_genesetEnrichment_analysis(req.query, genomes);
|
|
25
|
-
|
|
26
|
+
if (!req.query.geneset_name) {
|
|
27
|
+
res.send(results);
|
|
28
|
+
} else {
|
|
29
|
+
res.sendFile(results, (err) => {
|
|
30
|
+
fs.unlink(results, (del_err) => {
|
|
31
|
+
if (del_err) {
|
|
32
|
+
console.error("Error deleting file " + results + ":", del_err);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (err) {
|
|
36
|
+
res.status(404).send("Image not found");
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
26
40
|
} catch (e) {
|
|
27
41
|
res.send({ status: "error", error: e.message || e });
|
|
28
42
|
}
|
|
@@ -36,7 +50,10 @@ async function run_genesetEnrichment_analysis(q, genomes) {
|
|
|
36
50
|
fold_change: q.fold_change,
|
|
37
51
|
db: genomes[q.genome].termdbs.msigdb.cohort.db.connection.name,
|
|
38
52
|
// For now msigdb has been added, but later databases other than msigdb may be used
|
|
39
|
-
|
|
53
|
+
geneset_group: q.geneSetGroup,
|
|
54
|
+
cachedir: serverconfig.cachedir,
|
|
55
|
+
geneset_name: q.geneset_name,
|
|
56
|
+
pickle_file: q.pickle_file
|
|
40
57
|
};
|
|
41
58
|
const gsea_output = await run_gsea(
|
|
42
59
|
`${serverconfig.binpath}/utils/gsea.py`,
|
|
@@ -44,14 +61,25 @@ async function run_genesetEnrichment_analysis(q, genomes) {
|
|
|
44
61
|
// "/" is needed for python to accept the bracket "{" as a bracket
|
|
45
62
|
);
|
|
46
63
|
let result;
|
|
64
|
+
let data_found = false;
|
|
65
|
+
let image_found = false;
|
|
47
66
|
for (const line of gsea_output.split("\n")) {
|
|
48
67
|
if (line.startsWith("result: ")) {
|
|
49
68
|
result = JSON.parse(line.replace("result: ", ""));
|
|
69
|
+
data_found = true;
|
|
70
|
+
} else if (line.startsWith("image: ")) {
|
|
71
|
+
result = JSON.parse(line.replace("image: ", ""));
|
|
72
|
+
image_found = true;
|
|
50
73
|
} else {
|
|
51
74
|
console.log(line);
|
|
52
75
|
}
|
|
53
76
|
}
|
|
54
|
-
|
|
77
|
+
if (data_found) {
|
|
78
|
+
return result;
|
|
79
|
+
} else if (image_found) {
|
|
80
|
+
const imagePath = path.join(serverconfig.cachedir, result.image_file);
|
|
81
|
+
return imagePath;
|
|
82
|
+
}
|
|
55
83
|
}
|
|
56
84
|
async function run_gsea(path2, data) {
|
|
57
85
|
try {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import serverconfig from "#src/serverconfig.js";
|
|
4
|
+
const api = {
|
|
5
|
+
endpoint: "sampletiaimages",
|
|
6
|
+
methods: {
|
|
7
|
+
get: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: "GetSampleTiaImagesRequest"
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: "GetSampleTiaImagesResponse"
|
|
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 sampleTiaImagesPath = path.join(
|
|
33
|
+
`${serverconfig.tpmasterdir}/${ds.queries.TiaImages.imageBySampleFolder}`,
|
|
34
|
+
sampleId
|
|
35
|
+
);
|
|
36
|
+
console.log("sampleId", sampleId);
|
|
37
|
+
const sampleTiaImages = getTiaImages(sampleTiaImagesPath);
|
|
38
|
+
res.send({ sampleTiaImages });
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.log(e);
|
|
41
|
+
res.status(404).send("Sample images not found");
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function getTiaImages(sampleImagesPath) {
|
|
46
|
+
const files = fs.readdirSync(sampleImagesPath);
|
|
47
|
+
return files.filter((file) => [".svs", ".mrxs"].includes(path.extname(file)));
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
api
|
|
51
|
+
};
|
package/routes/termdb.config.js
CHANGED
|
@@ -182,6 +182,11 @@ function addNonDictionaryQueries(c, ds, genome) {
|
|
|
182
182
|
type: q.DZImages.type
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
|
+
if (q.TiaImages) {
|
|
186
|
+
q2.TiaImages = {
|
|
187
|
+
type: q.TiaImages.type
|
|
188
|
+
};
|
|
189
|
+
}
|
|
185
190
|
if (q.singleSampleGbtk) {
|
|
186
191
|
q2.singleSampleGbtk = {};
|
|
187
192
|
for (const k in q.singleSampleGbtk) {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import qs from "qs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import serverconfig from "#src/serverconfig.js";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
const routePath = "tiaimages";
|
|
7
|
+
const api = {
|
|
8
|
+
endpoint: `${routePath}`,
|
|
9
|
+
methods: {
|
|
10
|
+
get: {
|
|
11
|
+
init,
|
|
12
|
+
request: {
|
|
13
|
+
typeId: "any"
|
|
14
|
+
},
|
|
15
|
+
response: {
|
|
16
|
+
typeId: "any"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
post: {
|
|
20
|
+
alternativeFor: "get",
|
|
21
|
+
init
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function init({ genomes }) {
|
|
26
|
+
return async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const g = genomes[req.query.genome];
|
|
29
|
+
if (!g)
|
|
30
|
+
throw "invalid genome name";
|
|
31
|
+
const ds = g.datasets[req.query.dslabel];
|
|
32
|
+
if (!ds)
|
|
33
|
+
throw "invalid dataset name";
|
|
34
|
+
const sampleId = req.query.sampleId;
|
|
35
|
+
if (!sampleId)
|
|
36
|
+
throw "invalid sampleId";
|
|
37
|
+
const sessionResponse = await axios.get("http://127.0.0.1:5000/tileserver/session_id", { withCredentials: true });
|
|
38
|
+
const setCookieHeader = sessionResponse.headers["set-cookie"];
|
|
39
|
+
const sessionId = setCookieHeader && setCookieHeader[0].split(";")[0].split("=")[1];
|
|
40
|
+
console.log("sessionId", sessionId);
|
|
41
|
+
const sampleTiaImagesPath = path.join(
|
|
42
|
+
`${serverconfig.tpmasterdir}/${ds.queries.TiaImages.imageBySampleFolder}`,
|
|
43
|
+
sampleId
|
|
44
|
+
);
|
|
45
|
+
const images = getTiaImages(sampleTiaImagesPath);
|
|
46
|
+
const sampleTiaTileServer = path.join(
|
|
47
|
+
`${serverconfig.tileServerMount}/${ds.queries.TiaImages.imageBySampleFolder}`,
|
|
48
|
+
sampleId
|
|
49
|
+
);
|
|
50
|
+
const fname = `${sampleTiaTileServer}/${images[0]}`;
|
|
51
|
+
const data = qs.stringify({ slide_path: fname });
|
|
52
|
+
const putResponse = await axios.put("http://127.0.0.1:5000/tileserver/slide", data, {
|
|
53
|
+
withCredentials: true,
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
56
|
+
Cookie: `session_id=${sessionId}`
|
|
57
|
+
// Include the session_id in the headers
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const getResponse = await axios.get("http://127.0.0.1:5000/tileserver/slide", {
|
|
61
|
+
withCredentials: true,
|
|
62
|
+
headers: {
|
|
63
|
+
Cookie: `session_id=${sessionId}`
|
|
64
|
+
// Include the session_id in the headers
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
res.status(200).json({ sessionId, slide_dimensions: getResponse.data.slide_dimensions });
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.log(e);
|
|
70
|
+
res.send({
|
|
71
|
+
status: "error",
|
|
72
|
+
error: e.error || e
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function getTiaImages(sampleImagesPath) {
|
|
78
|
+
const files = fs.readdirSync(sampleImagesPath);
|
|
79
|
+
return files.filter((file) => [".svs", ".mrxs"].includes(path.extname(file)));
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
api
|
|
83
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import serverconfig from "#src/serverconfig.js";
|
|
3
|
+
const routePath = "tileserver";
|
|
4
|
+
const api = {
|
|
5
|
+
endpoint: `${routePath}/layer/slide/:sampleId/zoomify/:TileGroup/:z-:x-:y@1x.jpg`,
|
|
6
|
+
methods: {
|
|
7
|
+
get: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: "any"
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: "any"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
post: {
|
|
17
|
+
alternativeFor: "get",
|
|
18
|
+
init
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function init() {
|
|
23
|
+
return async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const { sampleId, TileGroup, z, x, y } = req.params;
|
|
26
|
+
const url = `${serverconfig.tileServerURL}/tileserver/layer/slide/${sampleId}/zoomify/${TileGroup}/${z}-${x}-${y}@1x.jpg`;
|
|
27
|
+
const response = await axios.get(url, { responseType: "arraybuffer" });
|
|
28
|
+
res.status(response.status).send(response.data);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (error.response) {
|
|
31
|
+
res.status(error.response.status).send(error.response.data);
|
|
32
|
+
} else {
|
|
33
|
+
res.status(500).send("Internal Server Error");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
api
|
|
40
|
+
};
|