@sjcrh/proteinpaint-server 2.70.4 → 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/dataset/termdb.test.js +14 -15
- package/package.json +3 -1
- package/routes/gdc.topMutatedGenes.js +1 -0
- 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 +831 -317
package/dataset/termdb.test.js
CHANGED
|
@@ -194,39 +194,38 @@ var termdb_test_default = {
|
|
|
194
194
|
topVariablyExpressedGenes: { src: "native" }
|
|
195
195
|
},
|
|
196
196
|
assayAvailability: {
|
|
197
|
-
//
|
|
197
|
+
// term used below must be annotated on samples rather than patients(root). otherwise matrix will pull wrong samples for geneVariant term
|
|
198
198
|
byDt: {
|
|
199
199
|
// snvindel, differentiating sample origin
|
|
200
200
|
1: {
|
|
201
201
|
byOrigin: {
|
|
202
202
|
germline: {
|
|
203
|
-
term_id: "
|
|
203
|
+
term_id: "assayavailability_germline",
|
|
204
204
|
label: "Germline",
|
|
205
205
|
// human readable label of this origin
|
|
206
|
-
yes: { value: ["
|
|
207
|
-
no: { value: ["
|
|
206
|
+
yes: { value: ["1"] },
|
|
207
|
+
no: { value: ["2"] }
|
|
208
208
|
},
|
|
209
209
|
somatic: {
|
|
210
|
-
term_id: "
|
|
210
|
+
term_id: "wgs_curated",
|
|
211
211
|
label: "Somatic",
|
|
212
|
-
yes: { value: ["
|
|
213
|
-
no: { value: ["
|
|
212
|
+
yes: { value: ["1"] },
|
|
213
|
+
no: { value: ["0"] }
|
|
214
|
+
// the category doesn't exist in termdb but is still supplied since somatic.no{} is required
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
},
|
|
217
218
|
// fusion
|
|
218
219
|
2: {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
no: { value: ["Asian Ancestry", "Multi-Ancestry-Admixed"] }
|
|
220
|
+
term_id: "assayavailability_fusion",
|
|
221
|
+
yes: { value: ["1"] },
|
|
222
|
+
no: { value: ["2"] }
|
|
223
223
|
},
|
|
224
224
|
// cnv
|
|
225
225
|
4: {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
no: { value: ["Asian Ancestry", "Multi-Ancestry-Admixed"] }
|
|
226
|
+
term_id: "assayavailability_cnv",
|
|
227
|
+
yes: { value: ["1"] },
|
|
228
|
+
no: { value: ["2"] }
|
|
230
229
|
}
|
|
231
230
|
}
|
|
232
231
|
}
|
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": {
|
|
@@ -218,6 +218,7 @@ const queryV2 = {
|
|
|
218
218
|
}
|
|
219
219
|
};
|
|
220
220
|
if (q.filter0) {
|
|
221
|
+
variables.ssmCase.content.push(JSON.parse(JSON.stringify(q.filter0)));
|
|
221
222
|
variables.caseFilters.content.push(JSON.parse(JSON.stringify(q.filter0)));
|
|
222
223
|
variables.geneCaseFilter.content.push(JSON.parse(JSON.stringify(q.filter0)));
|
|
223
224
|
variables.cnvLossFilters.content.push(JSON.parse(JSON.stringify(q.filter0)));
|
|
@@ -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
|
+
};
|