@sjcrh/proteinpaint-server 2.133.2 → 2.133.3-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.133.2",
3
+ "version": "2.133.3-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",
@@ -65,7 +65,7 @@
65
65
  "@sjcrh/proteinpaint-r": "2.130.0",
66
66
  "@sjcrh/proteinpaint-rust": "2.133.0",
67
67
  "@sjcrh/proteinpaint-shared": "2.132.1-2",
68
- "@sjcrh/proteinpaint-types": "2.133.0",
68
+ "@sjcrh/proteinpaint-types": "2.133.3-0",
69
69
  "@types/express": "^5.0.0",
70
70
  "@types/express-session": "^1.18.1",
71
71
  "better-sqlite3": "^9.4.1",
@@ -0,0 +1,33 @@
1
+ import { sampleWsiAiApiPayload } from "#types/checkers";
2
+ const routePath = "sampleWsiAiApi";
3
+ const api = {
4
+ endpoint: `${routePath}`,
5
+ methods: {
6
+ get: {
7
+ ...sampleWsiAiApiPayload,
8
+ init
9
+ },
10
+ post: {
11
+ ...sampleWsiAiApiPayload,
12
+ init
13
+ }
14
+ }
15
+ };
16
+ function init() {
17
+ return async (req, res) => {
18
+ try {
19
+ const request = req.query;
20
+ console.log("sample wsi api request:", request);
21
+ res.status(200).send({ testKey: "completed" });
22
+ } catch (e) {
23
+ console.warn(e);
24
+ res.status(500).send({
25
+ status: "error",
26
+ error: e.message || e
27
+ });
28
+ }
29
+ };
30
+ }
31
+ export {
32
+ api
33
+ };
@@ -1,4 +1,7 @@
1
1
  import { sampleWSImagesPayload } from "#types/checkers";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import serverconfig from "#src/serverconfig.js";
2
5
  const api = {
3
6
  endpoint: "samplewsimages",
4
7
  methods: {
@@ -26,14 +29,40 @@ function init({ genomes }) {
26
29
  const wsimages = await ds.queries.WSImages.getWSImages(sampleId);
27
30
  if (ds.queries.WSImages.getWSIAnnotations) {
28
31
  for (const wsimage of wsimages) {
32
+ if (ds.queries.WSImages.makeGeoJson) {
33
+ await ds.queries.WSImages.makeGeoJson(sampleId, wsimage);
34
+ }
29
35
  const annotations = await ds.queries.WSImages.getWSIAnnotations(sampleId, wsimage.filename);
30
- if (annotations) {
36
+ if (annotations && annotations.length > 0) {
31
37
  wsimage.overlays = annotations;
38
+ const annotationFilePath = path.join(
39
+ serverconfig.tpmasterdir,
40
+ ds.queries.WSImages.imageBySampleFolder,
41
+ sampleId,
42
+ annotations[0]
43
+ );
44
+ const annotationData = JSON.parse(fs.readFileSync(annotationFilePath, "utf8"));
45
+ if (!annotationData.features && !ds.queries.WSImages?.classes?.length) {
46
+ throw new Error(`No classes found for WSImage annotations in dataset ${ds.label}`);
47
+ }
48
+ wsimage.annotationsData = annotationData.features.map((d) => {
49
+ const featClass = ds.queries.WSImages?.classes?.find((f) => f.id == d.properties.class)?.label || d.properties.class;
50
+ return {
51
+ zoomCoordinates: d.properties.zoomCoordinates,
52
+ type: d.properties.type,
53
+ uncertainty: d.properties.uncertainty,
54
+ class: featClass
55
+ };
56
+ });
57
+ wsimage.classes = ds.queries?.WSImages?.classes;
58
+ wsimage.uncertainty = ds.queries?.WSImages?.uncertainty;
59
+ wsimage.activePatchColor = ds.queries?.WSImages?.activePatchColor || "#00e62a";
32
60
  }
33
61
  if (ds.queries.WSImages.getZoomInPoints) {
34
62
  const zoomInPoints = await ds.queries.WSImages.getZoomInPoints(
35
63
  sampleId,
36
- wsimage.filename
64
+ wsimage.filename,
65
+ query.index
37
66
  );
38
67
  if (zoomInPoints) {
39
68
  wsimage.zoomInPoints = zoomInPoints;
@@ -41,6 +70,14 @@ function init({ genomes }) {
41
70
  }
42
71
  }
43
72
  }
73
+ if (ds.queries.WSImages.getWSIPredictionOverlay) {
74
+ for (const wsimage of wsimages) {
75
+ const predictionOverlay = await ds.queries.WSImages.getWSIPredictionOverlay(sampleId, wsimage.filename);
76
+ if (predictionOverlay) {
77
+ wsimage.predictionLayers = [predictionOverlay];
78
+ }
79
+ }
80
+ }
44
81
  res.send({ sampleWSImages: wsimages });
45
82
  } catch (e) {
46
83
  console.log(e);
@@ -3,6 +3,7 @@ import qs from "qs";
3
3
  import path from "path";
4
4
  import { CookieJar } from "tough-cookie";
5
5
  import { promisify } from "util";
6
+ import { PredictionOverlayType } from "#types";
6
7
  import { wsImagesPayload } from "#types/checkers";
7
8
  import SessionManager from "../src/wsisessions/SessionManager.ts";
8
9
  import { ShardManager } from "#src/sharding/ShardManager.ts";
@@ -45,11 +46,16 @@ function init({ genomes }) {
45
46
  if (!mount)
46
47
  throw new Error("No mount available for TileServer");
47
48
  const wsiImagePath = path.join(`${mount}/${ds.queries.WSImages.imageBySampleFolder}/${sampleId}`, wsimage);
48
- const sessionId = await getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie, wsiImagePath);
49
- const getWsiImageResponse = await getWsiImageDimensions(sessionId, getCookieString, wsiImagePath);
49
+ const session = await getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie, wsiImagePath);
50
+ const getWsiImageResponse = await getWsiImageDimensions(
51
+ session.imageSessionId,
52
+ getCookieString,
53
+ wsiImagePath
54
+ );
50
55
  const payload = {
51
56
  status: "ok",
52
- wsiSessionId: sessionId,
57
+ wsiSessionId: session.imageSessionId,
58
+ overlays: session.overlays,
53
59
  slide_dimensions: getWsiImageResponse.slide_dimensions
54
60
  };
55
61
  res.status(200).json(payload);
@@ -67,9 +73,9 @@ async function getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie,
67
73
  const invalidateResult = await sessionManager.syncAndInvalidateSessions(wsimage);
68
74
  if (!invalidateResult)
69
75
  throw new Error("Session invalidation failed");
70
- const sessionData = await sessionManager.getSession(wsimage);
71
- if (sessionData) {
72
- return sessionData.imageSessionId;
76
+ const session = await sessionManager.getSession(wsimage);
77
+ if (session) {
78
+ return session;
73
79
  }
74
80
  const tileServer = await sessionManager.getTileServerShard(wsimage);
75
81
  if (!tileServer)
@@ -82,6 +88,7 @@ async function getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie,
82
88
  const sessionId = cookieString.match(/session_id=([^;]*)/)?.[1];
83
89
  if (!sessionId)
84
90
  throw new Error("session_id not found");
91
+ const overlays = [];
85
92
  const data = qs.stringify({ slide_path: wsimage });
86
93
  await ky.put(`${tileServer.url}/tileserver/slide`, {
87
94
  body: data,
@@ -92,25 +99,79 @@ async function getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie,
92
99
  },
93
100
  hooks: getHooks(cookieJar, getCookieString, setCookie)
94
101
  });
95
- await sessionManager.setSession(wsimage, sessionId, tileServer);
96
- if (ds.queries.WSImages.getWSIAnnotations) {
97
- const annotationFiles = await ds.queries.WSImages.getWSIAnnotations(sampleId, wsimage);
98
- if (!annotationFiles)
99
- throw new Error("No annotations files found");
102
+ if (ds.queries.WSImages.getWSIPredictionOverlay) {
103
+ const predictionOverlay = await ds.queries.WSImages.getWSIPredictionOverlay(sampleId, wsimage);
104
+ if (predictionOverlay) {
105
+ const mount = serverconfig.features?.tileserver?.mount;
106
+ const annotationsFilePath = path.join(
107
+ `${mount}/${ds.queries.WSImages.imageBySampleFolder}/${sampleId}`,
108
+ predictionOverlay
109
+ );
110
+ const annotationsData = qs.stringify({
111
+ overlay_path: annotationsFilePath
112
+ });
113
+ const layerNumber = await ky.put(`${tileServer.url}/tileserver/overlay`, {
114
+ body: annotationsData,
115
+ timeout: 5e4,
116
+ headers: {
117
+ "Content-Type": "application/x-www-form-urlencoded",
118
+ Cookie: `session_id=${sessionId}`
119
+ },
120
+ hooks: getHooks(cookieJar, getCookieString, setCookie)
121
+ }).json();
122
+ const overlay = {
123
+ layerNumber,
124
+ predictionOverlayType: PredictionOverlayType.PREDICTION
125
+ };
126
+ overlays.push(overlay);
127
+ }
128
+ }
129
+ if (ds.queries.WSImages.getWSIUncertaintyOverlay) {
130
+ const uncertaintyOverlay = await ds.queries.WSImages.getWSIUncertaintyOverlay(sampleId, wsimage);
131
+ if (uncertaintyOverlay) {
132
+ const mount = serverconfig.features?.tileserver?.mount;
133
+ const annotationsFilePath = path.join(
134
+ `${mount}/${ds.queries.WSImages.imageBySampleFolder}/${sampleId}`,
135
+ uncertaintyOverlay
136
+ );
137
+ const annotationsData = qs.stringify({
138
+ overlay_path: annotationsFilePath
139
+ });
140
+ const layerNumber = await ky.put(`${tileServer.url}/tileserver/overlay`, {
141
+ body: annotationsData,
142
+ timeout: 5e4,
143
+ headers: {
144
+ "Content-Type": "application/x-www-form-urlencoded",
145
+ Cookie: `session_id=${sessionId}`
146
+ },
147
+ hooks: getHooks(cookieJar, getCookieString, setCookie)
148
+ }).json();
149
+ const overlay = {
150
+ layerNumber,
151
+ predictionOverlayType: PredictionOverlayType.UNCERTAINTY
152
+ };
153
+ overlays.push(overlay);
154
+ }
155
+ }
156
+ const sessionData = await sessionManager.setSession(wsimage, sessionId, tileServer, overlays);
157
+ if (ds.queries.WSImages.getWSIPredictionPatches) {
158
+ const predictionPatches = await ds.queries.WSImages.getWSIPredictionPatches(sampleId, wsimage);
159
+ if (!predictionPatches)
160
+ throw new Error("No prediction files found");
100
161
  const mount = serverconfig.features?.tileserver?.mount;
101
162
  if (!mount)
102
163
  throw new Error("No mount available for TileServer");
103
- if (annotationFiles.length > 0) {
104
- for (const annotationFile of annotationFiles) {
105
- const annotationsFilePath = path.join(
164
+ if (predictionPatches.length > 0) {
165
+ for (const predictionPatch of predictionPatches) {
166
+ const predictionFilePath = path.join(
106
167
  `${mount}/${ds.queries.WSImages.imageBySampleFolder}/${sampleId}`,
107
- annotationFile
168
+ predictionPatch
108
169
  );
109
- const annotationsData = qs.stringify({
110
- overlay_path: annotationsFilePath
170
+ const predictionsData = qs.stringify({
171
+ overlay_path: predictionFilePath
111
172
  });
112
173
  await ky.put(`${tileServer.url}/tileserver/overlay`, {
113
- body: annotationsData,
174
+ body: predictionsData,
114
175
  timeout: 5e4,
115
176
  headers: {
116
177
  "Content-Type": "application/x-www-form-urlencoded",
@@ -121,8 +182,8 @@ async function getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie,
121
182
  }
122
183
  const cmapData = qs.stringify({
123
184
  cmap: JSON.stringify({
124
- keys: ["annotation"],
125
- values: [ds.queries.WSImages.annotationsColor]
185
+ keys: ["prediction", "annotation"],
186
+ values: [ds.queries.WSImages.predictionColor, ds.queries.WSImages.annotationsColor]
126
187
  })
127
188
  });
128
189
  await ky.put(`${tileServer.url}/tileserver/cmap`, {
@@ -136,7 +197,7 @@ async function getSessionId(ds, sampleId, cookieJar, getCookieString, setCookie,
136
197
  });
137
198
  }
138
199
  }
139
- return sessionId;
200
+ return sessionData;
140
201
  }
141
202
  async function getWsiImageDimensions(sessionId, getCookieString, wsimage) {
142
203
  const shardManager = ShardManager.getInstance();