@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.
@@ -194,39 +194,38 @@ var termdb_test_default = {
194
194
  topVariablyExpressedGenes: { src: "native" }
195
195
  },
196
196
  assayAvailability: {
197
- // use "genetic_race" as fake sources of assay availability
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: "genetic_race",
203
+ term_id: "assayavailability_germline",
204
204
  label: "Germline",
205
205
  // human readable label of this origin
206
- yes: { value: ["European Ancestry", "Asian Ancestry"] },
207
- no: { value: ["Multi-Ancestry-Admixed", "African Ancestry"] }
206
+ yes: { value: ["1"] },
207
+ no: { value: ["2"] }
208
208
  },
209
209
  somatic: {
210
- term_id: "genetic_race",
210
+ term_id: "wgs_curated",
211
211
  label: "Somatic",
212
- yes: { value: ["European Ancestry", "African Ancestry"] },
213
- no: { value: ["Multi-Ancestry-Admixed", "Asian Ancestry"] }
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
- //mutations are detected from RNAseq
220
- term_id: "genetic_race",
221
- yes: { value: ["European Ancestry", "African Ancestry"] },
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
- // mutations are detected from Methylation
227
- term_id: "genetic_race",
228
- yes: { value: ["European Ancestry", "African Ancestry"] },
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.70.4",
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
+ };
@@ -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 got.post(url, {
123
+ const response = await cachedFetch(url, {
124
+ method: "POST",
124
125
  headers,
125
- body: JSON.stringify(getGeneSelectionArg(q))
126
+ body: getGeneSelectionArg(q)
126
127
  });
127
- const re = JSON.parse(response.body);
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
+ };