@sjcrh/proteinpaint-server 2.71.0 → 2.72.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 +3 -1
- package/routes/samplewsimages.js +50 -0
- package/routes/termdb.config.js +6 -1
- package/routes/termdb.topVariablyExpressedGenes.js +5 -4
- package/routes/tileserver.js +42 -0
- package/routes/wsimages.js +115 -0
- package/src/app.js +829 -317
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.72.0",
|
|
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/tough-cookie": "^4.0.5",
|
|
47
48
|
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
|
48
49
|
"babel-loader": "^8.2.2",
|
|
49
50
|
"esbuild": "^0.19.12",
|
|
@@ -82,6 +83,7 @@
|
|
|
82
83
|
"node-fetch": "^2.6.1",
|
|
83
84
|
"partjson": "^0.58.2",
|
|
84
85
|
"tiny-async-pool": "^1.2.0",
|
|
86
|
+
"tough-cookie": "^4.1.4",
|
|
85
87
|
"typedoc-plugin-missing-exports": "^2.0.1"
|
|
86
88
|
},
|
|
87
89
|
"repository": {
|
|
@@ -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: "samplewsimages",
|
|
6
|
+
methods: {
|
|
7
|
+
get: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: "GetSampleWSImagesRequest"
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: "GetSampleWSImagesResponse"
|
|
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 sampleWSImagesPath = path.join(
|
|
33
|
+
`${serverconfig.tpmasterdir}/${ds.queries.WSImages.imageBySampleFolder}`,
|
|
34
|
+
sampleId
|
|
35
|
+
);
|
|
36
|
+
const sampleWSImages = await getWSImages(sampleWSImagesPath);
|
|
37
|
+
res.send({ sampleWSImages });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.log(e);
|
|
40
|
+
res.status(404).send("Sample images not found");
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function getWSImages(sampleImagesPath) {
|
|
45
|
+
const files = await fs.promises.readdir(sampleImagesPath);
|
|
46
|
+
return files.filter((file) => [".svs", ".mrxs", ".scn", ".ndpi", ".tiff"].includes(path.extname(file)));
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
api
|
|
50
|
+
};
|
package/routes/termdb.config.js
CHANGED
|
@@ -177,11 +177,16 @@ function addNonDictionaryQueries(c, ds, genome) {
|
|
|
177
177
|
q2.NIdata[k] = JSON.parse(JSON.stringify(q.NIdata[k]));
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
if (q.DZImages) {
|
|
180
|
+
if (q.DZImages && serverconfig.features.showDZImages) {
|
|
181
181
|
q2.DZImages = {
|
|
182
182
|
type: q.DZImages.type
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
|
+
if (q.WSImages && serverconfig.features.showWSImages) {
|
|
186
|
+
q2.WSImages = {
|
|
187
|
+
type: q.WSImages.type
|
|
188
|
+
};
|
|
189
|
+
}
|
|
185
190
|
if (q.singleSampleGbtk) {
|
|
186
191
|
q2.singleSampleGbtk = {};
|
|
187
192
|
for (const k in q.singleSampleGbtk) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { run_rust } from "@sjcrh/proteinpaint-rust";
|
|
3
|
-
import got from "got";
|
|
4
3
|
import serverconfig from "#src/serverconfig.js";
|
|
5
4
|
import { get_samples } from "#src/termdb.sql.js";
|
|
6
5
|
import { makeFilter } from "#src/mds3.gdc.js";
|
|
6
|
+
import { cachedFetch } from "#src/utils.js";
|
|
7
7
|
const api = {
|
|
8
8
|
endpoint: "termdb/topVariablyExpressedGenes",
|
|
9
9
|
methods: {
|
|
@@ -120,11 +120,12 @@ function gdcValidateQuery(ds, genome) {
|
|
|
120
120
|
const { host, headers } = ds.getHostHeaders(q);
|
|
121
121
|
const url = path.join(host.geneExp, "/gene_expression/gene_selection");
|
|
122
122
|
try {
|
|
123
|
-
const response = await
|
|
123
|
+
const response = await cachedFetch(url, {
|
|
124
|
+
method: "POST",
|
|
124
125
|
headers,
|
|
125
|
-
body:
|
|
126
|
+
body: getGeneSelectionArg(q)
|
|
126
127
|
});
|
|
127
|
-
const re =
|
|
128
|
+
const re = response.body;
|
|
128
129
|
const genes = [];
|
|
129
130
|
if (!Array.isArray(re.gene_selection))
|
|
130
131
|
throw "re.gene_selection[] is not array";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import ky from "ky";
|
|
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 ky.get(url);
|
|
28
|
+
const buffer = await response.arrayBuffer();
|
|
29
|
+
res.status(response.status).send(Buffer.from(buffer));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error.response) {
|
|
32
|
+
const errorBody = await error.response.arrayBuffer();
|
|
33
|
+
res.status(error.response.status).send(Buffer.from(errorBody));
|
|
34
|
+
} else {
|
|
35
|
+
res.status(500).send("Internal Server Error");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
api
|
|
42
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import ky from "ky";
|
|
2
|
+
import qs from "qs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import serverconfig from "#src/serverconfig.js";
|
|
5
|
+
import { CookieJar } from "tough-cookie";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
const routePath = "wsimages";
|
|
8
|
+
const api = {
|
|
9
|
+
endpoint: `${routePath}`,
|
|
10
|
+
methods: {
|
|
11
|
+
get: {
|
|
12
|
+
init,
|
|
13
|
+
request: {
|
|
14
|
+
typeId: "any"
|
|
15
|
+
},
|
|
16
|
+
response: {
|
|
17
|
+
typeId: "any"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
post: {
|
|
21
|
+
alternativeFor: "get",
|
|
22
|
+
init
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function init({ genomes }) {
|
|
27
|
+
return async (req, res) => {
|
|
28
|
+
try {
|
|
29
|
+
const g = genomes[req.query.genome];
|
|
30
|
+
if (!g)
|
|
31
|
+
throw "invalid genome name";
|
|
32
|
+
const ds = g.datasets[req.query.dslabel];
|
|
33
|
+
if (!ds)
|
|
34
|
+
throw "invalid dataset name";
|
|
35
|
+
const sampleId = req.query.sampleId;
|
|
36
|
+
if (!sampleId)
|
|
37
|
+
throw "invalid sampleId";
|
|
38
|
+
const wsimage = req.query.wsimage;
|
|
39
|
+
if (!wsimage)
|
|
40
|
+
throw "invalid wsimage";
|
|
41
|
+
const cookieJar = new CookieJar();
|
|
42
|
+
const setCookie = promisify(cookieJar.setCookie.bind(cookieJar));
|
|
43
|
+
const getCookieString = promisify(cookieJar.getCookieString.bind(cookieJar));
|
|
44
|
+
await ky.get(`${serverconfig.tileServerURL}/tileserver/session_id`, {
|
|
45
|
+
hooks: {
|
|
46
|
+
beforeRequest: [
|
|
47
|
+
async (request) => {
|
|
48
|
+
const cookie = await getCookieString(request.url);
|
|
49
|
+
request.headers.set("Cookie", cookie);
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
afterResponse: [
|
|
53
|
+
async (request, options, response) => {
|
|
54
|
+
const setCookieHeader = response.headers.get("set-cookie");
|
|
55
|
+
if (setCookieHeader) {
|
|
56
|
+
await setCookie(setCookieHeader, request.url);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const cookieString = await getCookieString(`${serverconfig.tileServerURL}/tileserver/session_id`);
|
|
63
|
+
const sessionId = cookieString.match(/session_id=([^;]*)/)?.[1];
|
|
64
|
+
const sampleWsiTileServer = path.join(
|
|
65
|
+
`${serverconfig.tileServerMount}/${ds.queries.WSImages.imageBySampleFolder}/${sampleId}`,
|
|
66
|
+
wsimage
|
|
67
|
+
);
|
|
68
|
+
const data = qs.stringify({ slide_path: sampleWsiTileServer });
|
|
69
|
+
await ky.put(`${serverconfig.tileServerURL}/tileserver/slide`, {
|
|
70
|
+
body: data,
|
|
71
|
+
headers: {
|
|
72
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
73
|
+
Cookie: `session_id=${sessionId}`
|
|
74
|
+
// Include the session_id in the headers
|
|
75
|
+
},
|
|
76
|
+
hooks: {
|
|
77
|
+
beforeRequest: [
|
|
78
|
+
async (request) => {
|
|
79
|
+
const cookie = await getCookieString(request.url);
|
|
80
|
+
request.headers.set("Cookie", cookie);
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
afterResponse: [
|
|
84
|
+
async (request, options, response) => {
|
|
85
|
+
const setCookieHeader = response.headers.get("set-cookie");
|
|
86
|
+
if (setCookieHeader) {
|
|
87
|
+
await setCookie(setCookieHeader, request.url);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const getWsiImageResponse = await ky.get(`${serverconfig.tileServerURL}/tileserver/slide`, {
|
|
94
|
+
hooks: {
|
|
95
|
+
beforeRequest: [
|
|
96
|
+
async (request) => {
|
|
97
|
+
const cookie = await getCookieString(request.url);
|
|
98
|
+
request.headers.set("Cookie", cookie);
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
}).json();
|
|
103
|
+
res.status(200).json({ sessionId, slide_dimensions: getWsiImageResponse.slide_dimensions });
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.log(e);
|
|
106
|
+
res.send({
|
|
107
|
+
status: "error",
|
|
108
|
+
error: e.error || e
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
api
|
|
115
|
+
};
|