@sjcrh/proteinpaint-server 2.146.4-1 → 2.147.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/dataset/termdb.test.js +1 -4
- package/package.json +3 -3
- package/routes/aiProjectSelectedWSImages.js +2 -24
- package/routes/aiProjectTrainModel.js +61 -0
- package/routes/termdb.cohort.summary.js +2 -0
- package/routes/termdb.filterTermValues.js +2 -1
- package/routes/termdb.profileFormScores.js +6 -38
- package/routes/termdb.profileScores.js +41 -31
- package/routes/termdb.violin.js +25 -28
- package/routes/wsimages.js +32 -48
- package/src/app.js +1910 -1760
package/dataset/termdb.test.js
CHANGED
|
@@ -287,10 +287,7 @@ function termdb_test_default() {
|
|
|
287
287
|
}
|
|
288
288
|
},
|
|
289
289
|
cnv: {
|
|
290
|
-
file: "files/hg38/TermdbTest/TermdbTest_CNV_gene.gz"
|
|
291
|
-
cnvMaxLength: 2e7,
|
|
292
|
-
cnvGainCutoff: 0.1,
|
|
293
|
-
cnvLossCutoff: -0.1
|
|
290
|
+
file: "files/hg38/TermdbTest/TermdbTest_CNV_gene.gz"
|
|
294
291
|
},
|
|
295
292
|
singleSampleMutation: {
|
|
296
293
|
src: "native",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.147.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",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@sjcrh/proteinpaint-python": "2.146.0",
|
|
65
65
|
"@sjcrh/proteinpaint-r": "2.146.4-0",
|
|
66
66
|
"@sjcrh/proteinpaint-rust": "2.146.4-1",
|
|
67
|
-
"@sjcrh/proteinpaint-shared": "2.
|
|
68
|
-
"@sjcrh/proteinpaint-types": "2.
|
|
67
|
+
"@sjcrh/proteinpaint-shared": "2.147.1",
|
|
68
|
+
"@sjcrh/proteinpaint-types": "2.147.1",
|
|
69
69
|
"@types/express": "^5.0.0",
|
|
70
70
|
"@types/express-session": "^1.18.1",
|
|
71
71
|
"better-sqlite3": "^9.4.1",
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import serverconfig from "#src/serverconfig.js";
|
|
4
1
|
import { aiProjectSelectedWSImagesResponsePayload } from "#types/checkers";
|
|
5
2
|
import { getDbConnection } from "#src/aiHistoDBConnection.ts";
|
|
6
3
|
const api = {
|
|
@@ -37,31 +34,12 @@ function init({ genomes }) {
|
|
|
37
34
|
wsimage.uncertainty = ds.queries?.WSImages?.uncertainty;
|
|
38
35
|
wsimage.activePatchColor = ds.queries?.WSImages?.activePatchColor;
|
|
39
36
|
if (ds.queries.WSImages.getWSIPredictionPatches) {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
if (!mount) throw new Error("No mount available for TileServer");
|
|
43
|
-
const predictionsFilePath = path.join(mount, ds.queries.WSImages.aiToolImageFolder, predictionsFile[0]);
|
|
44
|
-
const predictionsData = JSON.parse(fs.readFileSync(predictionsFilePath, "utf8"));
|
|
45
|
-
wsimage.predictions = predictionsData.features.map((d) => {
|
|
46
|
-
const featClass = wsimage.classes?.find((f) => f.id == d.properties.class)?.label;
|
|
47
|
-
return {
|
|
48
|
-
zoomCoordinates: d.properties.zoomCoordinates,
|
|
49
|
-
uncertainty: d.properties.uncertainty,
|
|
50
|
-
class: featClass
|
|
51
|
-
};
|
|
52
|
-
});
|
|
37
|
+
const predictions = await ds.queries.WSImages.getWSIPredictionPatches(projectId, wsimageFilename);
|
|
38
|
+
wsimage.predictions = predictions;
|
|
53
39
|
}
|
|
54
40
|
wsimages.push(wsimage);
|
|
55
41
|
}
|
|
56
42
|
}
|
|
57
|
-
if (ds.queries.WSImages.getWSIPredictionOverlay) {
|
|
58
|
-
for (const wsimage of wsimages) {
|
|
59
|
-
const predictionOverlay = await ds.queries.WSImages.getWSIPredictionOverlay(wsimage.filename);
|
|
60
|
-
if (predictionOverlay) {
|
|
61
|
-
wsimage.predictionLayers = [predictionOverlay];
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
43
|
res.send({ wsimages });
|
|
66
44
|
} catch (e) {
|
|
67
45
|
console.log(e);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { aiProjectTrainModelPayload } from "#types/checkers";
|
|
2
|
+
import { TileServerSessionsHandler } from "#src/wsisessions/TileServerSessionsHandler.ts";
|
|
3
|
+
import SessionManager from "#src/wsisessions/SessionManager.ts";
|
|
4
|
+
const api = {
|
|
5
|
+
endpoint: "aiProjectTrainModel",
|
|
6
|
+
methods: {
|
|
7
|
+
get: {
|
|
8
|
+
...aiProjectTrainModelPayload,
|
|
9
|
+
init
|
|
10
|
+
},
|
|
11
|
+
post: {
|
|
12
|
+
...aiProjectTrainModelPayload,
|
|
13
|
+
init
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function init({ genomes }) {
|
|
18
|
+
return async function(req, res) {
|
|
19
|
+
try {
|
|
20
|
+
const query = req.query;
|
|
21
|
+
console.log(query);
|
|
22
|
+
const g = genomes[query.genome];
|
|
23
|
+
if (!g) throw "invalid genome name";
|
|
24
|
+
const ds = g.datasets[query.dslabel];
|
|
25
|
+
if (!ds) throw "invalid dataset name";
|
|
26
|
+
if (typeof ds.queries.WSImages.retrainModel == "function") {
|
|
27
|
+
await ds.queries.WSImages.retrainModel(query.projectId);
|
|
28
|
+
try {
|
|
29
|
+
const handler = new TileServerSessionsHandler();
|
|
30
|
+
const sessionMgr = SessionManager.getInstance();
|
|
31
|
+
const keySessions = await sessionMgr.getAllKeyValues();
|
|
32
|
+
const sessions = keySessions.map((kv) => kv?.sessionData).filter(Boolean);
|
|
33
|
+
if (sessions && sessions.length) {
|
|
34
|
+
await handler.resetSessions(sessions);
|
|
35
|
+
await Promise.all(keySessions.map((kv) => sessionMgr.deleteSession(kv.key)));
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.warn("TileServerSessionsHandler error:", err);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
res.status(500).send({
|
|
42
|
+
status: "error",
|
|
43
|
+
error: "No retraining script defined"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
res.status(200).send({
|
|
47
|
+
status: "ok",
|
|
48
|
+
message: `Retraining model completed`
|
|
49
|
+
});
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.warn(e);
|
|
52
|
+
res.status(500).send({
|
|
53
|
+
status: "error",
|
|
54
|
+
error: e.message || e
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
api
|
|
61
|
+
};
|
|
@@ -2,6 +2,7 @@ import { termdbCohortSummaryPayload } from "#types/checkers";
|
|
|
2
2
|
import { get_ds_tdb } from "#src/termdb.js";
|
|
3
3
|
import { mayCopyFromCookie } from "#src/utils.js";
|
|
4
4
|
import { get_samples } from "#src/termdb.sql.js";
|
|
5
|
+
import { authApi } from "#src/auth.js";
|
|
5
6
|
const api = {
|
|
6
7
|
endpoint: "termdb/cohort/summary",
|
|
7
8
|
methods: {
|
|
@@ -19,6 +20,7 @@ function init({ genomes }) {
|
|
|
19
20
|
const genome = genomes[q.genome];
|
|
20
21
|
if (!genome) throw "invalid genome";
|
|
21
22
|
const [ds] = get_ds_tdb(genome, q);
|
|
23
|
+
authApi.mayAdjustFilter(req.query, ds, []);
|
|
22
24
|
let count;
|
|
23
25
|
if (q.filter?.lst?.length) {
|
|
24
26
|
const samples = await get_samples(q, ds);
|
|
@@ -55,7 +55,8 @@ function getList(samplesPerFilter, filtersData, tw, showAll) {
|
|
|
55
55
|
const sampleValues = Array.from(new Set(annotations));
|
|
56
56
|
const filteredValues = [];
|
|
57
57
|
for (const value of values) {
|
|
58
|
-
|
|
58
|
+
let label = value.label.replace(/["']/g, "");
|
|
59
|
+
if (label.length > 50) label = label.slice(0, 47) + "...";
|
|
59
60
|
const disabled = !sampleValues.includes(value.key || value.label);
|
|
60
61
|
if (!showAll && disabled) continue;
|
|
61
62
|
filteredValues.push({ value: value.key || value.label, label, disabled });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProfileFormScoresPayload } from "#types/checkers";
|
|
2
|
-
import {
|
|
2
|
+
import { getScoresData } from "./termdb.profileScores.js";
|
|
3
3
|
const api = {
|
|
4
4
|
endpoint: "termdb/profileFormScores",
|
|
5
5
|
methods: {
|
|
@@ -28,56 +28,24 @@ function init({ genomes }) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
async function getScoresDict(query, ds) {
|
|
31
|
-
if (!query.filterByUserSites) query.__protected__.ignoredTermIds.push(query.facilityTW.term.id);
|
|
32
31
|
const terms = [...query.scoreTerms, query.facilityTW];
|
|
33
32
|
if (query.scScoreTerms) terms.push(...query.scScoreTerms);
|
|
34
|
-
const data = await
|
|
35
|
-
{
|
|
36
|
-
terms,
|
|
37
|
-
filter: query.site || !query.isAggregate ? void 0 : query.filter,
|
|
38
|
-
//if isRadarFacility and site is specified, do not apply the filter
|
|
39
|
-
__protected__: query.__protected__
|
|
40
|
-
},
|
|
41
|
-
ds
|
|
42
|
-
);
|
|
43
|
-
const lst = Object.values(data.samples);
|
|
44
|
-
let sites = lst.map((s) => {
|
|
45
|
-
return { label: data.refs.bySampleId[s.sample].label, value: s.sample };
|
|
46
|
-
});
|
|
47
|
-
sites = lst.map((s) => {
|
|
48
|
-
return { label: data.refs.bySampleId[s.sample].label, value: s.sample };
|
|
49
|
-
});
|
|
50
|
-
if (query.userSites) {
|
|
51
|
-
sites = sites.filter((s) => query.userSites.includes(s.label));
|
|
52
|
-
if (lst.length == 1 && !sites.length) {
|
|
53
|
-
const siteId = lst[0].sample;
|
|
54
|
-
const site = data.refs.bySampleId[siteId].label;
|
|
55
|
-
const hospital2 = query.facilityTW.term.values[site].label;
|
|
56
|
-
throw `The access to the hospital ${hospital2} is denied`;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
let sitesSelected = [];
|
|
60
|
-
if (query.site) sitesSelected = [query.site];
|
|
61
|
-
else if (!query.isAggregate) sitesSelected = [sites[0].value];
|
|
62
|
-
else sitesSelected = query.sites;
|
|
63
|
-
const sampleData = sitesSelected?.length == 1 ? data.samples[sitesSelected[0]] : null;
|
|
64
|
-
let samples = sampleData ? [sampleData] : Object.values(data.samples);
|
|
65
|
-
if (sitesSelected?.length > 0) samples = samples.filter((s) => sitesSelected.includes(s.sample));
|
|
33
|
+
const data = await getScoresData(query, ds, terms);
|
|
66
34
|
const term2Score = {};
|
|
67
35
|
for (const d of query.scoreTerms) {
|
|
68
36
|
const getDictFunc = (sample) => getDict(d.$id, sample);
|
|
69
|
-
const percents = getPercentsDict(getDictFunc, samples);
|
|
37
|
+
const percents = getPercentsDict(getDictFunc, data.samples);
|
|
70
38
|
term2Score[d.term.id] = percents;
|
|
71
39
|
}
|
|
72
40
|
if (query.scScoreTerms)
|
|
73
41
|
for (const d of query.scScoreTerms) {
|
|
74
|
-
const percents = getSCPercentsDict(d, samples);
|
|
42
|
+
const percents = getSCPercentsDict(d, data.samples);
|
|
75
43
|
term2Score[d.term.id] = percents;
|
|
76
44
|
}
|
|
77
|
-
const facilityValue = sampleData?.[query.facilityTW.$id];
|
|
45
|
+
const facilityValue = data.sampleData?.[query.facilityTW.$id];
|
|
78
46
|
const termValue = query.facilityTW.term.values[facilityValue?.value];
|
|
79
47
|
const hospital = termValue?.label || termValue?.key;
|
|
80
|
-
return { term2Score, sites, hospital, n: sampleData ? 1 : samples.length };
|
|
48
|
+
return { term2Score, sites: data.sites, hospital, n: data.sampleData ? 1 : data.samples.length };
|
|
81
49
|
}
|
|
82
50
|
function getDict(key, sample) {
|
|
83
51
|
if (!sample[key]) return null;
|
|
@@ -27,52 +27,61 @@ function init({ genomes }) {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
async function
|
|
30
|
+
async function getScoresData(query, ds, terms) {
|
|
31
31
|
if (!query.filterByUserSites) query.__protected__.ignoredTermIds.push(query.facilityTW.term.id);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
terms.push(term.score);
|
|
35
|
-
if (term.maxScore?.term) {
|
|
36
|
-
terms.push(term.maxScore);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
32
|
+
const { clientAuthResult, activeCohort } = query.__protected__;
|
|
33
|
+
const userSites = clientAuthResult[activeCohort].sites;
|
|
39
34
|
const data = await getData(
|
|
40
35
|
{
|
|
41
36
|
terms,
|
|
42
|
-
filter: query.
|
|
43
|
-
//if site is specified, do not apply the filter that is for the aggregation
|
|
37
|
+
filter: query.filter,
|
|
44
38
|
__protected__: query.__protected__
|
|
45
39
|
},
|
|
46
40
|
ds
|
|
47
41
|
);
|
|
42
|
+
if (data.error) throw data.error;
|
|
48
43
|
const lst = Object.values(data.samples);
|
|
49
44
|
let sites = lst.map((s) => {
|
|
50
|
-
|
|
45
|
+
let label = query.facilityTW.term.values[s[query.facilityTW.$id].value]?.label || s[query.facilityTW.$id].value;
|
|
46
|
+
if (label.length > 50) label = label.slice(0, 47) + "...";
|
|
47
|
+
return {
|
|
48
|
+
value: s[query.facilityTW.$id].value,
|
|
49
|
+
label
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
if (userSites) {
|
|
53
|
+
sites = sites.filter((s) => userSites.includes(s.value));
|
|
54
|
+
}
|
|
55
|
+
sites.sort((a, b) => {
|
|
56
|
+
return a.label.localeCompare(b.label);
|
|
51
57
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
const samples = Object.values(data.samples);
|
|
59
|
+
let sampleData, site;
|
|
60
|
+
if ("facilitySite" in query) {
|
|
61
|
+
const facilitySite = query.facilitySite || sites[0].value;
|
|
62
|
+
const index = lst.findIndex((s) => s[query.facilityTW.$id].value == facilitySite);
|
|
63
|
+
sampleData = lst[index];
|
|
64
|
+
site = sites.find((s) => s.value == facilitySite);
|
|
65
|
+
} else if (sites.length == 1) {
|
|
66
|
+
sampleData = data.samples[sites[0].value];
|
|
67
|
+
site = sites[0];
|
|
68
|
+
}
|
|
69
|
+
return { samples, sampleData, sites, site };
|
|
70
|
+
}
|
|
71
|
+
async function getScores(query, ds) {
|
|
72
|
+
const terms = [query.facilityTW];
|
|
73
|
+
for (const term of query.scoreTerms) {
|
|
74
|
+
terms.push(term.score);
|
|
75
|
+
if (term.maxScore?.term) {
|
|
76
|
+
terms.push(term.maxScore);
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
|
-
|
|
62
|
-
if (query.site) sitesSelected = [query.site];
|
|
63
|
-
else if (!query.isAggregate) sitesSelected = [sites[0].value];
|
|
64
|
-
else sitesSelected = query.sites;
|
|
65
|
-
const sampleData = sitesSelected?.length == 1 ? data.samples[sitesSelected[0]] : null;
|
|
66
|
-
let samples = Object.values(data.samples);
|
|
67
|
-
if (sitesSelected?.length > 0) samples = samples.filter((s) => sitesSelected.includes(s.sample));
|
|
79
|
+
const data = await getScoresData(query, ds, terms);
|
|
68
80
|
const term2Score = {};
|
|
69
81
|
for (const d of query.scoreTerms) {
|
|
70
|
-
term2Score[d.score.term.id] = getPercentage(d, samples, sampleData);
|
|
82
|
+
term2Score[d.score.term.id] = getPercentage(d, data.samples, data.sampleData);
|
|
71
83
|
}
|
|
72
|
-
|
|
73
|
-
const termValue = query.facilityTW.term.values[facilityValue?.value];
|
|
74
|
-
const hospital = termValue?.label || termValue?.key;
|
|
75
|
-
return { term2Score, sites, hospital, n: sampleData ? 1 : samples.length };
|
|
84
|
+
return { term2Score, sites: data.sites, site: data.site, n: data.sampleData ? 1 : data.samples.length };
|
|
76
85
|
}
|
|
77
86
|
function getPercentage(d, samples, sampleData) {
|
|
78
87
|
if (!d) return null;
|
|
@@ -91,5 +100,6 @@ function getPercentage(d, samples, sampleData) {
|
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
export {
|
|
94
|
-
api
|
|
103
|
+
api,
|
|
104
|
+
getScoresData
|
|
95
105
|
};
|
package/routes/termdb.violin.js
CHANGED
|
@@ -30,7 +30,7 @@ function init({ genomes }) {
|
|
|
30
30
|
if (!g) throw "invalid genome name";
|
|
31
31
|
const ds = g.datasets?.[q.dslabel];
|
|
32
32
|
if (!ds) throw "invalid ds";
|
|
33
|
-
data = await
|
|
33
|
+
data = await getViolin(q, ds);
|
|
34
34
|
} catch (e) {
|
|
35
35
|
data = { error: e?.message || e };
|
|
36
36
|
if (e instanceof Error && e.stack) console.log(e);
|
|
@@ -38,7 +38,7 @@ function init({ genomes }) {
|
|
|
38
38
|
res.send(data);
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
async function
|
|
41
|
+
async function getViolin(q, ds) {
|
|
42
42
|
if (typeof q.tw?.term != "object" || typeof q.tw?.q != "object") throw "q.tw not of {term,q}";
|
|
43
43
|
const term = q.tw.term;
|
|
44
44
|
if (!q.tw.q.mode) q.tw.q.mode = "continuous";
|
|
@@ -175,7 +175,7 @@ async function createCanvasImg(q, result, ds) {
|
|
|
175
175
|
if (q.radius <= 0) throw "q.radius is not a number";
|
|
176
176
|
else q.radius = +q.radius;
|
|
177
177
|
if (!q.strokeWidth) q.strokeWidth = 0.2;
|
|
178
|
-
const
|
|
178
|
+
const isH = q.orientation == "horizontal";
|
|
179
179
|
for (const k of Object.keys(result.charts)) {
|
|
180
180
|
const chart = result.charts[k];
|
|
181
181
|
const plot2Values = {};
|
|
@@ -184,46 +184,44 @@ async function createCanvasImg(q, result, ds) {
|
|
|
184
184
|
let axisScale;
|
|
185
185
|
const useLog = q.unit == "log";
|
|
186
186
|
if (useLog) {
|
|
187
|
-
axisScale = scaleLog().base(ds.cohort.termdb.logscaleBase2 ? 2 : 10).domain([result.min, result.max]).range(
|
|
187
|
+
axisScale = scaleLog().base(ds.cohort.termdb.logscaleBase2 ? 2 : 10).domain([result.min, result.max]).range(isH ? [0, q.svgw] : [q.svgw, 0]);
|
|
188
188
|
} else {
|
|
189
|
-
axisScale = scaleLinear().domain([result.min, result.max]).range(
|
|
189
|
+
axisScale = scaleLinear().domain([result.min, result.max]).range(isH ? [0, q.svgw] : [q.svgw, 0]);
|
|
190
190
|
}
|
|
191
|
-
const [width, height] =
|
|
192
|
-
const scaledRadius = q.radius / q.devicePixelRatio;
|
|
193
|
-
const arcEndAngle = scaledRadius * Math.PI;
|
|
191
|
+
const [width, height] = isH ? [q.svgw * q.devicePixelRatio, q.radius * q.devicePixelRatio] : [q.radius * q.devicePixelRatio, q.svgw * q.devicePixelRatio];
|
|
194
192
|
for (const plot of chart.plots) {
|
|
195
193
|
plot.density = densities[plot.label];
|
|
196
|
-
const noDensityRendered = plot.density.densityMax == 0;
|
|
197
194
|
const canvas = createCanvas(width, height);
|
|
198
195
|
const ctx = canvas.getContext("2d");
|
|
199
|
-
|
|
200
|
-
ctx.strokeStyle =
|
|
201
|
-
ctx.lineWidth =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (q.devicePixelRatio != 1) {
|
|
205
|
-
ctx.scale(q.devicePixelRatio, q.devicePixelRatio);
|
|
206
|
-
}
|
|
207
|
-
if (q.datasymbol === "rug")
|
|
196
|
+
if (q.devicePixelRatio != 1) ctx.scale(q.devicePixelRatio, q.devicePixelRatio);
|
|
197
|
+
ctx.strokeStyle = "black";
|
|
198
|
+
ctx.lineWidth = 1;
|
|
199
|
+
if (q.datasymbol === "rug") {
|
|
200
|
+
ctx.globalAlpha = 0.8;
|
|
208
201
|
plot.values.forEach((i) => {
|
|
202
|
+
const s = axisScale(i);
|
|
209
203
|
ctx.beginPath();
|
|
210
|
-
if (
|
|
211
|
-
ctx.moveTo(
|
|
212
|
-
ctx.lineTo(
|
|
204
|
+
if (isH) {
|
|
205
|
+
ctx.moveTo(s, 0);
|
|
206
|
+
ctx.lineTo(s, q.radius);
|
|
213
207
|
} else {
|
|
214
|
-
ctx.moveTo(0,
|
|
215
|
-
ctx.lineTo(
|
|
208
|
+
ctx.moveTo(0, s);
|
|
209
|
+
ctx.lineTo(q.radius, s);
|
|
216
210
|
}
|
|
217
211
|
ctx.stroke();
|
|
218
212
|
});
|
|
219
|
-
else if (q.datasymbol === "bean")
|
|
213
|
+
} else if (q.datasymbol === "bean") {
|
|
214
|
+
ctx.globalAlpha = 0.6;
|
|
215
|
+
ctx.fillStyle = "#ffe6e6";
|
|
220
216
|
plot.values.forEach((i) => {
|
|
217
|
+
const s = axisScale(i);
|
|
221
218
|
ctx.beginPath();
|
|
222
|
-
if (
|
|
223
|
-
else ctx.arc(q.radius,
|
|
219
|
+
if (isH) ctx.arc(s, q.radius / 2, q.radius / 2, 0, 2 * Math.PI);
|
|
220
|
+
else ctx.arc(q.radius / 2, s, q.radius / 2, 0, 2 * Math.PI);
|
|
224
221
|
ctx.fill();
|
|
225
222
|
ctx.stroke();
|
|
226
223
|
});
|
|
224
|
+
}
|
|
227
225
|
plot.src = canvas.toDataURL();
|
|
228
226
|
plot.summaryStats = getDescrStats(plot.values);
|
|
229
227
|
}
|
|
@@ -264,6 +262,5 @@ export {
|
|
|
264
262
|
getDensities,
|
|
265
263
|
getDensity,
|
|
266
264
|
getWilcoxonData,
|
|
267
|
-
sortPlot2Values
|
|
268
|
-
trigger_getViolinPlotData
|
|
265
|
+
sortPlot2Values
|
|
269
266
|
};
|
package/routes/wsimages.js
CHANGED
|
@@ -40,7 +40,7 @@ function init({ genomes }) {
|
|
|
40
40
|
const setCookie = promisify(cookieJar.setCookie.bind(cookieJar));
|
|
41
41
|
const getCookieString = promisify(cookieJar.getCookieString.bind(cookieJar));
|
|
42
42
|
const wsiImagePath = await getWSImagePath(ds, wSImagesRequest);
|
|
43
|
-
const session = await getSessionId(ds, cookieJar, getCookieString, setCookie, wsiImagePath);
|
|
43
|
+
const session = await getSessionId(ds, cookieJar, getCookieString, setCookie, wsiImagePath, aiProjectId);
|
|
44
44
|
const getWsiImageResponse = await getWsiImageDimensions(
|
|
45
45
|
session.imageSessionId,
|
|
46
46
|
getCookieString,
|
|
@@ -74,7 +74,7 @@ async function getWSImagePath(ds, wSImagesRequest) {
|
|
|
74
74
|
return path.join(`${mount}/${ds.queries.WSImages.aiToolImageFolder}/`, wSImagesRequest.wsimage);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
async function getSessionId(ds, cookieJar, getCookieString, setCookie, wsimage) {
|
|
77
|
+
async function getSessionId(ds, cookieJar, getCookieString, setCookie, wsimage, projectId) {
|
|
78
78
|
const sessionManager = SessionManager.getInstance();
|
|
79
79
|
const invalidateResult = await sessionManager.syncAndInvalidateSessions(wsimage);
|
|
80
80
|
if (!invalidateResult) throw new Error("Session invalidation failed");
|
|
@@ -102,55 +102,39 @@ async function getSessionId(ds, cookieJar, getCookieString, setCookie, wsimage)
|
|
|
102
102
|
},
|
|
103
103
|
hooks: getHooks(cookieJar, getCookieString, setCookie)
|
|
104
104
|
});
|
|
105
|
-
if (ds.queries.WSImages.
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
105
|
+
if (ds.queries.WSImages.getPredictionLayers) {
|
|
106
|
+
const predictionLayers = await ds.queries.WSImages.getPredictionLayers(projectId, wsimage);
|
|
107
|
+
if (predictionLayers) {
|
|
108
108
|
const mount = serverconfig.features?.tileserver?.mount;
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
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: "Prediction"
|
|
109
|
+
const resolveFilename = (key) => {
|
|
110
|
+
if (!predictionLayers) return void 0;
|
|
111
|
+
if (predictionLayers instanceof Map) return predictionLayers.get(key) ?? void 0;
|
|
112
|
+
return predictionLayers[key] ?? void 0;
|
|
125
113
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
hooks: getHooks(cookieJar, getCookieString, setCookie)
|
|
148
|
-
}).json();
|
|
149
|
-
const overlay = {
|
|
150
|
-
layerNumber,
|
|
151
|
-
predictionOverlayType: "Uncertainty"
|
|
114
|
+
const pushOverlayForKey = async (key, predictionOverlayType) => {
|
|
115
|
+
const filename = resolveFilename(key);
|
|
116
|
+
if (!filename) return;
|
|
117
|
+
const overlayFilePath = path.join(`${mount}/${ds.queries.WSImages.aiToolImageFolder}/`, filename);
|
|
118
|
+
const annotationsData = qs.stringify({
|
|
119
|
+
overlay_path: overlayFilePath
|
|
120
|
+
});
|
|
121
|
+
const layerNumber = await ky.put(`${tileServer.url}/tileserver/overlay`, {
|
|
122
|
+
body: annotationsData,
|
|
123
|
+
timeout: 5e4,
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
126
|
+
Cookie: `session_id=${sessionId}`
|
|
127
|
+
},
|
|
128
|
+
hooks: getHooks(cookieJar, getCookieString, setCookie)
|
|
129
|
+
}).json();
|
|
130
|
+
const overlay = {
|
|
131
|
+
layerNumber,
|
|
132
|
+
predictionOverlayType
|
|
133
|
+
};
|
|
134
|
+
overlays.push(overlay);
|
|
152
135
|
};
|
|
153
|
-
|
|
136
|
+
await pushOverlayForKey("Prediction", "Prediction");
|
|
137
|
+
await pushOverlayForKey("Uncertainty", "Uncertainty");
|
|
154
138
|
}
|
|
155
139
|
}
|
|
156
140
|
const sessionData = await sessionManager.setSession(wsimage, sessionId, tileServer, overlays);
|