@sjcrh/proteinpaint-server 2.189.0 → 2.190.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 +9 -12
- package/routes/aiProjectAdmin.js +2 -28
- package/routes/aiProjectSelectedWSImages.js +2 -16
- package/routes/brainImaging.js +1 -15
- package/routes/brainImagingSamples.js +1 -15
- package/routes/burden.js +1 -15
- package/routes/correlationVolcano.js +1 -15
- package/routes/dataset.js +1 -16
- package/routes/deleteWSITileSelection.js +2 -12
- package/routes/dsdata.js +1 -19
- package/routes/gdc.grin2.list.js +1 -15
- package/routes/gdc.grin2.run.js +1 -15
- package/routes/gdc.maf.js +1 -15
- package/routes/gdc.mafBuild.js +1 -15
- package/routes/genesetEnrichment.js +129 -97
- package/routes/grin2.js +110 -79
- package/routes/saveWSIAnnotation.js +2 -13
- package/routes/termdb.DE.js +137 -54
- package/routes/termdb.categories.js +2 -16
- package/routes/termdb.chat.js +169 -1076
- package/routes/termdb.cluster.js +5 -16
- package/routes/termdb.config.js +12 -17
- package/routes/termdb.descrstats.js +2 -16
- package/routes/termdb.diffMeth.js +100 -21
- package/routes/termdb.geneRanking.js +139 -0
- package/routes/termdb.proteome.js +1 -15
- package/routes/termdb.runChart.js +16 -30
- package/routes/termdb.sampleScatter.js +7 -97
- package/routes/termdb.singleCellPlots.js +159 -0
- package/routes/termdb.singlecellSamples.js +6 -16
- package/routes/termdb.violinBox.js +1 -15
- package/routes/wsimages.js +1 -16
- package/src/app.js +3984 -4103
- package/routes/_template_.js +0 -33
- package/routes/aiProjectTrainModel.js +0 -68
- package/routes/alphaGenome.js +0 -41
- package/routes/alphaGenomeTypes.js +0 -36
- package/routes/dzimages.js +0 -55
- package/routes/gene2canonicalisoform.js +0 -37
- package/routes/genelookup.js +0 -32
- package/routes/genesetOverrepresentation.js +0 -49
- package/routes/genomes.js +0 -150
- package/routes/healthcheck.js +0 -35
- package/routes/hicdata.js +0 -74
- package/routes/hicgenome.js +0 -75
- package/routes/hicstat.js +0 -35
- package/routes/img.js +0 -46
- package/routes/isoformlst.js +0 -48
- package/routes/ntseq.js +0 -36
- package/routes/pdomain.js +0 -53
- package/routes/profile.barchart2.js +0 -114
- package/routes/profile.forms2.js +0 -107
- package/routes/profile.polar2.js +0 -101
- package/routes/profile.radar2.js +0 -112
- package/routes/profile.radarFacility2.js +0 -148
- package/routes/sampledzimages.js +0 -48
- package/routes/samplewsimages.js +0 -60
- package/routes/snp.js +0 -98
- package/routes/termdb.chat2.js +0 -217
- package/routes/termdb.chat3.js +0 -209
- package/routes/termdb.cohort.summary.js +0 -37
- package/routes/termdb.cohorts.js +0 -41
- package/routes/termdb.dapVolcano.js +0 -80
- package/routes/termdb.dmr.js +0 -93
- package/routes/termdb.filterTermValues.js +0 -89
- package/routes/termdb.isoformAvailability.js +0 -35
- package/routes/termdb.numericcategories.js +0 -46
- package/routes/termdb.percentile.js +0 -66
- package/routes/termdb.profileFormScores.js +0 -92
- package/routes/termdb.profileScores.js +0 -113
- package/routes/termdb.rootterm.js +0 -39
- package/routes/termdb.sampleImages.js +0 -63
- package/routes/termdb.singleSampleMutation.js +0 -75
- package/routes/termdb.singlecellDEgenes.js +0 -55
- package/routes/termdb.singlecellData.js +0 -39
- package/routes/termdb.termchildren.js +0 -42
- package/routes/termdb.termsbyids.js +0 -50
- package/routes/termdb.topMutatedGenes.js +0 -127
- package/routes/termdb.topTermsByType.js +0 -96
- package/routes/termdb.topVariablyExpressedGenes.js +0 -132
- package/routes/tileserver.js +0 -68
- package/routes/wsisamples.js +0 -71
package/routes/hicstat.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { fileurl, file_is_readable } from "#src/utils.js";
|
|
2
|
-
import { do_hicstat } from "#src/hicstat.ts";
|
|
3
|
-
import { hicstatPayload } from "#types/checkers";
|
|
4
|
-
const api = {
|
|
5
|
-
endpoint: "hicstat",
|
|
6
|
-
methods: {
|
|
7
|
-
get: {
|
|
8
|
-
...hicstatPayload,
|
|
9
|
-
init
|
|
10
|
-
},
|
|
11
|
-
post: {
|
|
12
|
-
...hicstatPayload,
|
|
13
|
-
init
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
function init() {
|
|
18
|
-
return async (req, res) => {
|
|
19
|
-
try {
|
|
20
|
-
const [e, file, isurl] = fileurl(req);
|
|
21
|
-
if (e) throw "illegal file name";
|
|
22
|
-
if (!isurl) {
|
|
23
|
-
await file_is_readable(file);
|
|
24
|
-
}
|
|
25
|
-
const out = await do_hicstat(file, isurl);
|
|
26
|
-
res.send({ out });
|
|
27
|
-
} catch (e) {
|
|
28
|
-
res.send({ error: e instanceof Error ? e.message : e });
|
|
29
|
-
if (e instanceof Error && e.stack) console.log(e);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export {
|
|
34
|
-
api
|
|
35
|
-
};
|
package/routes/img.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { imgPayload } from "#types/checkers";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import * as utils from "../src/utils.js";
|
|
5
|
-
import { imageSize } from "image-size";
|
|
6
|
-
const api = {
|
|
7
|
-
endpoint: "img",
|
|
8
|
-
methods: {
|
|
9
|
-
get: {
|
|
10
|
-
...imgPayload,
|
|
11
|
-
init
|
|
12
|
-
},
|
|
13
|
-
post: {
|
|
14
|
-
...imgPayload,
|
|
15
|
-
init
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
function init() {
|
|
20
|
-
return async (req, res) => {
|
|
21
|
-
try {
|
|
22
|
-
sendImage(req, res);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
res.send({ status: "error", error: e.message || e });
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
async function sendImage(req, res) {
|
|
29
|
-
const [e, file] = utils.fileurl(req, true);
|
|
30
|
-
try {
|
|
31
|
-
if (e) throw "invalid image file";
|
|
32
|
-
const data = await fs.promises.readFile(file);
|
|
33
|
-
const ext = path.extname(file).substring(1);
|
|
34
|
-
const { width, height } = imageSize(file);
|
|
35
|
-
const image = {
|
|
36
|
-
src: `data:image/${ext};base64,${Buffer.from(data).toString("base64")}`,
|
|
37
|
-
size: `${width}x${height}`
|
|
38
|
-
};
|
|
39
|
-
res.send({ ...image });
|
|
40
|
-
} catch (e2) {
|
|
41
|
-
res.send({ error: e2.message || e2 });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
export {
|
|
45
|
-
api
|
|
46
|
-
};
|
package/routes/isoformlst.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { isoformlstPayload } from "#types/checkers";
|
|
2
|
-
const api = {
|
|
3
|
-
// route endpoint
|
|
4
|
-
// - no need for trailing slash
|
|
5
|
-
// - should be a noun (method is based on HTTP GET, POST, etc)
|
|
6
|
-
// - don't add 'Data' as response is assumed to be data
|
|
7
|
-
endpoint: "isoformlst",
|
|
8
|
-
methods: {
|
|
9
|
-
get: {
|
|
10
|
-
...isoformlstPayload,
|
|
11
|
-
init
|
|
12
|
-
},
|
|
13
|
-
post: {
|
|
14
|
-
...isoformlstPayload,
|
|
15
|
-
init
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
function init({ genomes }) {
|
|
20
|
-
return function handle_isoformlst(req, res) {
|
|
21
|
-
try {
|
|
22
|
-
const q = req.query;
|
|
23
|
-
const g = genomes[q.genome];
|
|
24
|
-
if (!g) throw "invalid genome";
|
|
25
|
-
if (!Array.isArray(q.lst)) throw ".lst missing";
|
|
26
|
-
const lst = [];
|
|
27
|
-
for (const isoform of q.lst) {
|
|
28
|
-
if (g.genomicNameRegexp.test(isoform)) continue;
|
|
29
|
-
const tmp = g.genedb.getjsonbyisoform.all(isoform);
|
|
30
|
-
lst.push(
|
|
31
|
-
tmp.map((i) => {
|
|
32
|
-
const j = JSON.parse(i.genemodel);
|
|
33
|
-
if (i.isdefault) j.isdefault = true;
|
|
34
|
-
return j;
|
|
35
|
-
})
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
res.send({ lst });
|
|
39
|
-
} catch (e) {
|
|
40
|
-
res.send({ error: e.message || e });
|
|
41
|
-
if (e.stack) console.log(e.stack);
|
|
42
|
-
else console.trace(e);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
export {
|
|
47
|
-
api
|
|
48
|
-
};
|
package/routes/ntseq.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { ntseqPayload } from "#types/checkers";
|
|
2
|
-
import { get_fasta } from "#src/utils.js";
|
|
3
|
-
const api = {
|
|
4
|
-
endpoint: "ntseq",
|
|
5
|
-
methods: {
|
|
6
|
-
get: {
|
|
7
|
-
...ntseqPayload,
|
|
8
|
-
init
|
|
9
|
-
},
|
|
10
|
-
post: {
|
|
11
|
-
...ntseqPayload,
|
|
12
|
-
init
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
function init({ genomes }) {
|
|
17
|
-
return async function handle_ntseq(req, res) {
|
|
18
|
-
try {
|
|
19
|
-
const q = req.query;
|
|
20
|
-
if (!q.coord) throw "coord missing";
|
|
21
|
-
const g = genomes[q.genome];
|
|
22
|
-
if (!g) throw "invalid genome";
|
|
23
|
-
if (!g.genomefile) throw "no sequence file available";
|
|
24
|
-
const seq = await get_fasta(g, q.coord);
|
|
25
|
-
res.send({
|
|
26
|
-
seq: seq.split("\n").slice(1).join("")
|
|
27
|
-
});
|
|
28
|
-
} catch (e) {
|
|
29
|
-
res.send({ error: e.message || e });
|
|
30
|
-
if (e.stack) console.log(e.stack);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export {
|
|
35
|
-
api
|
|
36
|
-
};
|
package/routes/pdomain.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { pdomainPayload } from "#types/checkers";
|
|
2
|
-
const api = {
|
|
3
|
-
// route endpoint
|
|
4
|
-
// - no need for trailing slash
|
|
5
|
-
// - should be a noun (method is based on HTTP GET, POST, etc)
|
|
6
|
-
// - don't add 'Data' as response is assumed to be data
|
|
7
|
-
endpoint: "pdomain",
|
|
8
|
-
methods: {
|
|
9
|
-
get: {
|
|
10
|
-
...pdomainPayload,
|
|
11
|
-
init
|
|
12
|
-
},
|
|
13
|
-
post: {
|
|
14
|
-
...pdomainPayload,
|
|
15
|
-
init
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
function init({ genomes }) {
|
|
20
|
-
return function handle_pdomain(req, res) {
|
|
21
|
-
try {
|
|
22
|
-
const q = req.query;
|
|
23
|
-
const gn = q.genome;
|
|
24
|
-
if (!gn) throw "no genome";
|
|
25
|
-
const g = genomes[gn];
|
|
26
|
-
if (!g) throw "invalid genome " + gn;
|
|
27
|
-
if (!g.proteindomain) {
|
|
28
|
-
return res.send({ lst: [] });
|
|
29
|
-
}
|
|
30
|
-
if (!Array.isArray(q.isoforms)) throw "isoforms[] missing";
|
|
31
|
-
const lst = [];
|
|
32
|
-
for (const isoform of q.isoforms) {
|
|
33
|
-
if (g.genomicNameRegexp.test(isoform)) continue;
|
|
34
|
-
const tmp = g.proteindomain.getbyisoform.all(isoform);
|
|
35
|
-
lst.push({
|
|
36
|
-
name: isoform,
|
|
37
|
-
pdomains: tmp.map((i) => {
|
|
38
|
-
const j = JSON.parse(i.data);
|
|
39
|
-
j.refseq = isoform;
|
|
40
|
-
return j;
|
|
41
|
-
})
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
res.send({ lst });
|
|
45
|
-
} catch (e) {
|
|
46
|
-
res.send({ error: e.message || e });
|
|
47
|
-
if (e.stack) console.log(e.stack);
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
export {
|
|
52
|
-
api
|
|
53
|
-
};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { ProfileScoresPayload } from "#types/checkers";
|
|
2
|
-
import { getData } from "../src/termdb.matrix.js";
|
|
3
|
-
const api = {
|
|
4
|
-
endpoint: "termdb/profileBarchart2Scores",
|
|
5
|
-
methods: {
|
|
6
|
-
get: {
|
|
7
|
-
...ProfileScoresPayload,
|
|
8
|
-
init
|
|
9
|
-
},
|
|
10
|
-
post: {
|
|
11
|
-
...ProfileScoresPayload,
|
|
12
|
-
init
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
function init({ genomes }) {
|
|
17
|
-
return async (req, res) => {
|
|
18
|
-
try {
|
|
19
|
-
const g = genomes[req.query.genome];
|
|
20
|
-
if (!g) throw "invalid genome name";
|
|
21
|
-
const ds = g.datasets?.[req.query.dslabel];
|
|
22
|
-
const result = await getScores(req.query, ds);
|
|
23
|
-
res.send(result);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
console.log(e);
|
|
26
|
-
res.send({ status: "error", error: e.message || e });
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function derivePrefix(query) {
|
|
31
|
-
const firstScoreId = query.scoreTerms?.[0]?.score?.term?.id;
|
|
32
|
-
if (firstScoreId?.startsWith("F")) return "F";
|
|
33
|
-
if (firstScoreId?.startsWith("A")) return "A";
|
|
34
|
-
for (const entry of query.filter?.lst || []) {
|
|
35
|
-
const id = entry.tvs?.term?.id;
|
|
36
|
-
if (id?.startsWith("F")) return "F";
|
|
37
|
-
if (id?.startsWith("A")) return "A";
|
|
38
|
-
}
|
|
39
|
-
throw "cannot determine cohort prefix from scoreTerms or filter term IDs";
|
|
40
|
-
}
|
|
41
|
-
async function getScores(query, ds) {
|
|
42
|
-
const { activeCohort, clientAuthResult } = query.__protected__;
|
|
43
|
-
const prefix = derivePrefix(query);
|
|
44
|
-
const facilityTermId = `${prefix}UNIT`;
|
|
45
|
-
const facilityTW = { term: { id: facilityTermId }, q: {} };
|
|
46
|
-
const terms = [facilityTW];
|
|
47
|
-
for (const t of query.scoreTerms) {
|
|
48
|
-
terms.push(t.score);
|
|
49
|
-
if (t.maxScore?.term) terms.push(t.maxScore);
|
|
50
|
-
}
|
|
51
|
-
if (!query.filterByUserSites) {
|
|
52
|
-
query.__protected__.ignoredTermIds.push(facilityTermId);
|
|
53
|
-
}
|
|
54
|
-
const cohortAuth = clientAuthResult[activeCohort];
|
|
55
|
-
const isPublic = !cohortAuth?.role || cohortAuth.role === "public";
|
|
56
|
-
const userSites = cohortAuth?.sites;
|
|
57
|
-
const raw = await getData(
|
|
58
|
-
{
|
|
59
|
-
terms,
|
|
60
|
-
filter: query.filter,
|
|
61
|
-
__protected__: query.__protected__
|
|
62
|
-
},
|
|
63
|
-
ds
|
|
64
|
-
);
|
|
65
|
-
if (raw.error) throw raw.error;
|
|
66
|
-
const sampleList = Object.values(raw.samples);
|
|
67
|
-
let sites = sampleList.map((s) => {
|
|
68
|
-
const val = s[facilityTW.$id].value;
|
|
69
|
-
let label = facilityTW.term.values?.[val]?.label || val;
|
|
70
|
-
if (label.length > 50) label = label.slice(0, 47) + "...";
|
|
71
|
-
return { value: val, label };
|
|
72
|
-
});
|
|
73
|
-
if (userSites && query.filterByUserSites) {
|
|
74
|
-
sites = sites.filter((s) => userSites.includes(s.value));
|
|
75
|
-
}
|
|
76
|
-
sites.sort((a, b) => a.label.localeCompare(b.label));
|
|
77
|
-
const samples = Object.values(raw.samples);
|
|
78
|
-
const eligibleSamples = userSites && query.filterByUserSites ? samples.filter((s) => userSites.includes(s[facilityTW.$id].value)) : samples;
|
|
79
|
-
const term2Score = {};
|
|
80
|
-
for (const d of query.scoreTerms) {
|
|
81
|
-
const score = computeMedianPercentage(d, eligibleSamples);
|
|
82
|
-
if (score !== null) term2Score[d.score.term.id] = score;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
term2Score,
|
|
86
|
-
// Public users see only aggregated scores — do not expose site IDs or names
|
|
87
|
-
sites: isPublic ? [] : sites,
|
|
88
|
-
n: eligibleSamples.length
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
function computeMedianPercentage(d, samples) {
|
|
92
|
-
const percentages = [];
|
|
93
|
-
for (const s of samples) {
|
|
94
|
-
const scoreValue = s[d.score.$id]?.value;
|
|
95
|
-
if (scoreValue == null) continue;
|
|
96
|
-
let maxScoreValue = null;
|
|
97
|
-
if (typeof d.maxScore === "number") {
|
|
98
|
-
maxScoreValue = d.maxScore;
|
|
99
|
-
} else {
|
|
100
|
-
maxScoreValue = s[d.maxScore.$id]?.value;
|
|
101
|
-
}
|
|
102
|
-
if (maxScoreValue == null || maxScoreValue === 0) continue;
|
|
103
|
-
const percentage = scoreValue / maxScoreValue * 100;
|
|
104
|
-
percentages.push(percentage);
|
|
105
|
-
}
|
|
106
|
-
if (percentages.length === 0) return null;
|
|
107
|
-
percentages.sort((a, b) => a - b);
|
|
108
|
-
const mid = Math.floor(percentages.length / 2);
|
|
109
|
-
const median = percentages.length % 2 !== 0 ? percentages[mid] : (percentages[mid - 1] + percentages[mid]) / 2;
|
|
110
|
-
return Math.round(median);
|
|
111
|
-
}
|
|
112
|
-
export {
|
|
113
|
-
api
|
|
114
|
-
};
|
package/routes/profile.forms2.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { ProfileForms2ScoresPayload } from "#types/checkers";
|
|
2
|
-
import { getData } from "../src/termdb.matrix.js";
|
|
3
|
-
const api = {
|
|
4
|
-
endpoint: "termdb/profileForms2Scores",
|
|
5
|
-
methods: {
|
|
6
|
-
get: {
|
|
7
|
-
...ProfileForms2ScoresPayload,
|
|
8
|
-
init
|
|
9
|
-
},
|
|
10
|
-
post: {
|
|
11
|
-
...ProfileForms2ScoresPayload,
|
|
12
|
-
init
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
function init({ genomes }) {
|
|
17
|
-
return async (req, res) => {
|
|
18
|
-
try {
|
|
19
|
-
const g = genomes[req.query.genome];
|
|
20
|
-
if (!g) throw "invalid genome name";
|
|
21
|
-
const ds = g.datasets?.[req.query.dslabel];
|
|
22
|
-
const result = await getScores(req.query, ds);
|
|
23
|
-
res.send(result);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
console.log(e);
|
|
26
|
-
res.send({ status: "error", error: e.message || e });
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
async function getScores(query, ds) {
|
|
31
|
-
const { activeCohort, clientAuthResult } = query.__protected__;
|
|
32
|
-
const facilityTermId = ds.cohort.termdb.plotConfigByCohort?.[activeCohort]?.profileForms2?.facilityTW?.id;
|
|
33
|
-
if (!facilityTermId) throw `profileForms2.facilityTW.id missing for cohort '${activeCohort}'`;
|
|
34
|
-
const facilityTW = { term: { id: facilityTermId }, q: {} };
|
|
35
|
-
const terms = [facilityTW, ...query.scoreTerms];
|
|
36
|
-
if (query.scScoreTerms) terms.push(...query.scScoreTerms);
|
|
37
|
-
if (!query.filterByUserSites) query.__protected__.ignoredTermIds.push(facilityTermId);
|
|
38
|
-
const cohortAuth = clientAuthResult[activeCohort];
|
|
39
|
-
const isPublic = !cohortAuth?.role || cohortAuth.role === "public";
|
|
40
|
-
const userSites = cohortAuth?.sites;
|
|
41
|
-
const raw = await getData(
|
|
42
|
-
{
|
|
43
|
-
terms,
|
|
44
|
-
filter: query.filter,
|
|
45
|
-
__protected__: query.__protected__
|
|
46
|
-
},
|
|
47
|
-
ds
|
|
48
|
-
);
|
|
49
|
-
if (raw.error) throw raw.error;
|
|
50
|
-
const samples = Object.values(raw.samples);
|
|
51
|
-
let sites = samples.map((s) => {
|
|
52
|
-
const val = s[facilityTW.$id].value;
|
|
53
|
-
let label = facilityTW.term.values?.[val]?.label || val;
|
|
54
|
-
if (label.length > 50) label = label.slice(0, 47) + "...";
|
|
55
|
-
return { value: val, label };
|
|
56
|
-
});
|
|
57
|
-
if (userSites && query.filterByUserSites) sites = sites.filter((s) => userSites.includes(s.value));
|
|
58
|
-
sites.sort((a, b) => a.label.localeCompare(b.label));
|
|
59
|
-
const eligibleSamples = userSites && query.filterByUserSites ? samples.filter((s) => userSites.includes(s[facilityTW.$id].value)) : samples;
|
|
60
|
-
const term2Score = {};
|
|
61
|
-
for (const d of query.scoreTerms) {
|
|
62
|
-
term2Score[d.term.id] = getPercentsDict((sample) => getDict(d.$id, sample), eligibleSamples);
|
|
63
|
-
}
|
|
64
|
-
if (query.scScoreTerms) {
|
|
65
|
-
for (const d of query.scScoreTerms) {
|
|
66
|
-
term2Score[d.term.id] = getSCPercentsDict(d, eligibleSamples);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
term2Score,
|
|
71
|
-
sites: isPublic ? [] : sites,
|
|
72
|
-
// public never sees site IDs
|
|
73
|
-
n: eligibleSamples.length
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function getDict(key, sample) {
|
|
77
|
-
if (!sample[key]) return null;
|
|
78
|
-
const termData = sample[key].value;
|
|
79
|
-
return JSON.parse(termData);
|
|
80
|
-
}
|
|
81
|
-
function getPercentsDict(getDictFunc, samples) {
|
|
82
|
-
const percentageDict = {};
|
|
83
|
-
for (const sample of samples) {
|
|
84
|
-
const percents = getDictFunc(sample);
|
|
85
|
-
if (!percents) continue;
|
|
86
|
-
for (const key in percents) {
|
|
87
|
-
const value = percents[key];
|
|
88
|
-
if (!percentageDict[key]) percentageDict[key] = 0;
|
|
89
|
-
percentageDict[key] += value;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return percentageDict;
|
|
93
|
-
}
|
|
94
|
-
function getSCPercentsDict(tw, samples) {
|
|
95
|
-
if (!tw) throw "tw not defined";
|
|
96
|
-
const percentageDict = {};
|
|
97
|
-
for (const sample of samples) {
|
|
98
|
-
const key = sample[tw.$id]?.value;
|
|
99
|
-
if (key == null) continue;
|
|
100
|
-
if (!percentageDict[key]) percentageDict[key] = 0;
|
|
101
|
-
percentageDict[key] += 1;
|
|
102
|
-
}
|
|
103
|
-
return percentageDict;
|
|
104
|
-
}
|
|
105
|
-
export {
|
|
106
|
-
api
|
|
107
|
-
};
|
package/routes/profile.polar2.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { ProfileScoresPayload } from "#types/checkers";
|
|
2
|
-
import { getData } from "../src/termdb.matrix.js";
|
|
3
|
-
const api = {
|
|
4
|
-
endpoint: "termdb/profilePolar2Scores",
|
|
5
|
-
methods: {
|
|
6
|
-
get: {
|
|
7
|
-
...ProfileScoresPayload,
|
|
8
|
-
init
|
|
9
|
-
},
|
|
10
|
-
post: {
|
|
11
|
-
...ProfileScoresPayload,
|
|
12
|
-
init
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
function init({ genomes }) {
|
|
17
|
-
return async (req, res) => {
|
|
18
|
-
try {
|
|
19
|
-
const g = genomes[req.query.genome];
|
|
20
|
-
if (!g) throw "invalid genome name";
|
|
21
|
-
const ds = g.datasets?.[req.query.dslabel];
|
|
22
|
-
const result = await getScores(req.query, ds);
|
|
23
|
-
res.send(result);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
console.log(e);
|
|
26
|
-
res.send({ status: "error", error: e.message || e });
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function derivePrefix(query) {
|
|
31
|
-
const firstScoreId = query.scoreTerms?.[0]?.score?.term?.id;
|
|
32
|
-
if (firstScoreId?.startsWith("F")) return "F";
|
|
33
|
-
if (firstScoreId?.startsWith("A")) return "A";
|
|
34
|
-
for (const entry of query.filter?.lst || []) {
|
|
35
|
-
const id = entry.tvs?.term?.id;
|
|
36
|
-
if (id?.startsWith("F")) return "F";
|
|
37
|
-
if (id?.startsWith("A")) return "A";
|
|
38
|
-
}
|
|
39
|
-
throw "cannot determine cohort prefix from scoreTerms or filter term IDs";
|
|
40
|
-
}
|
|
41
|
-
async function getScores(query, ds) {
|
|
42
|
-
const { activeCohort, clientAuthResult } = query.__protected__;
|
|
43
|
-
const prefix = derivePrefix(query);
|
|
44
|
-
const facilityTermId = `${prefix}UNIT`;
|
|
45
|
-
const facilityTW = { term: { id: facilityTermId }, q: {} };
|
|
46
|
-
const terms = [facilityTW];
|
|
47
|
-
for (const t of query.scoreTerms) {
|
|
48
|
-
terms.push(t.score);
|
|
49
|
-
if (t.maxScore?.term) terms.push(t.maxScore);
|
|
50
|
-
}
|
|
51
|
-
if (!query.filterByUserSites) {
|
|
52
|
-
query.__protected__.ignoredTermIds.push(facilityTermId);
|
|
53
|
-
}
|
|
54
|
-
const cohortAuth = clientAuthResult[activeCohort];
|
|
55
|
-
const isPublic = !cohortAuth?.role || cohortAuth.role === "public";
|
|
56
|
-
const userSites = cohortAuth?.sites;
|
|
57
|
-
const raw = await getData(
|
|
58
|
-
{
|
|
59
|
-
terms,
|
|
60
|
-
filter: query.filter,
|
|
61
|
-
__protected__: query.__protected__
|
|
62
|
-
},
|
|
63
|
-
ds
|
|
64
|
-
);
|
|
65
|
-
if (raw.error) throw raw.error;
|
|
66
|
-
const sampleList = Object.values(raw.samples);
|
|
67
|
-
let sites = sampleList.map((s) => {
|
|
68
|
-
const val = s[facilityTW.$id].value;
|
|
69
|
-
let label = facilityTW.term.values?.[val]?.label || val;
|
|
70
|
-
if (label.length > 50) label = label.slice(0, 47) + "...";
|
|
71
|
-
return { value: val, label };
|
|
72
|
-
});
|
|
73
|
-
if (userSites && query.filterByUserSites) {
|
|
74
|
-
sites = sites.filter((s) => userSites.includes(s.value));
|
|
75
|
-
}
|
|
76
|
-
sites.sort((a, b) => a.label.localeCompare(b.label));
|
|
77
|
-
const samples = Object.values(raw.samples);
|
|
78
|
-
const eligibleSamples = userSites && query.filterByUserSites ? samples.filter((s) => userSites.includes(s[facilityTW.$id].value)) : samples;
|
|
79
|
-
const term2Score = {};
|
|
80
|
-
for (const d of query.scoreTerms) {
|
|
81
|
-
const score = computeMedianPercentage(d, eligibleSamples);
|
|
82
|
-
if (score !== null) term2Score[d.score.term.id] = score;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
term2Score,
|
|
86
|
-
// Public users see only aggregated scores — do not expose site IDs or names
|
|
87
|
-
sites: isPublic ? [] : sites,
|
|
88
|
-
n: eligibleSamples.length
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
function computeMedianPercentage(d, samples) {
|
|
92
|
-
const percentages = samples.filter((s) => s[d.score.$id]?.value != null).map((s) => s[d.score.$id].value / (s[d.maxScore.$id]?.value || d.maxScore) * 100);
|
|
93
|
-
if (percentages.length === 0) return null;
|
|
94
|
-
percentages.sort((a, b) => a - b);
|
|
95
|
-
const mid = Math.floor(percentages.length / 2);
|
|
96
|
-
const median = percentages.length % 2 !== 0 ? percentages[mid] : (percentages[mid - 1] + percentages[mid]) / 2;
|
|
97
|
-
return Math.round(median);
|
|
98
|
-
}
|
|
99
|
-
export {
|
|
100
|
-
api
|
|
101
|
-
};
|
package/routes/profile.radar2.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { ProfileScoresPayload } from "#types/checkers";
|
|
2
|
-
import { getData } from "../src/termdb.matrix.js";
|
|
3
|
-
const api = {
|
|
4
|
-
endpoint: "termdb/profileRadar2Scores",
|
|
5
|
-
methods: {
|
|
6
|
-
get: {
|
|
7
|
-
...ProfileScoresPayload,
|
|
8
|
-
init
|
|
9
|
-
},
|
|
10
|
-
post: {
|
|
11
|
-
...ProfileScoresPayload,
|
|
12
|
-
init
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
function init({ genomes }) {
|
|
17
|
-
return async (req, res) => {
|
|
18
|
-
try {
|
|
19
|
-
const g = genomes[req.query.genome];
|
|
20
|
-
if (!g) throw "invalid genome name";
|
|
21
|
-
const ds = g.datasets?.[req.query.dslabel];
|
|
22
|
-
const result = await getScores(req.query, ds);
|
|
23
|
-
res.send(result);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
console.log(e);
|
|
26
|
-
res.send({ status: "error", error: e.message || e });
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function derivePrefix(query) {
|
|
31
|
-
const firstScoreId = query.scoreTerms?.[0]?.score?.term?.id;
|
|
32
|
-
if (firstScoreId?.startsWith("F")) return "F";
|
|
33
|
-
if (firstScoreId?.startsWith("A")) return "A";
|
|
34
|
-
for (const entry of query.filter?.lst || []) {
|
|
35
|
-
const id = entry.tvs?.term?.id;
|
|
36
|
-
if (id?.startsWith("F")) return "F";
|
|
37
|
-
if (id?.startsWith("A")) return "A";
|
|
38
|
-
}
|
|
39
|
-
throw "cannot determine cohort prefix from scoreTerms or filter term IDs";
|
|
40
|
-
}
|
|
41
|
-
async function getScores(query, ds) {
|
|
42
|
-
const { activeCohort, clientAuthResult } = query.__protected__;
|
|
43
|
-
const prefix = derivePrefix(query);
|
|
44
|
-
const facilityTermId = `${prefix}UNIT`;
|
|
45
|
-
const facilityTW = { term: { id: facilityTermId }, q: {} };
|
|
46
|
-
const terms = [facilityTW];
|
|
47
|
-
for (const t of query.scoreTerms) {
|
|
48
|
-
terms.push(t.score);
|
|
49
|
-
if (t.maxScore?.term) terms.push(t.maxScore);
|
|
50
|
-
}
|
|
51
|
-
if (!query.filterByUserSites) {
|
|
52
|
-
query.__protected__.ignoredTermIds.push(facilityTermId);
|
|
53
|
-
}
|
|
54
|
-
const cohortAuth = clientAuthResult[activeCohort];
|
|
55
|
-
const isPublic = !cohortAuth?.role || cohortAuth.role === "public";
|
|
56
|
-
const userSites = cohortAuth?.sites;
|
|
57
|
-
const raw = await getData(
|
|
58
|
-
{
|
|
59
|
-
terms,
|
|
60
|
-
filter: query.filter,
|
|
61
|
-
__protected__: query.__protected__
|
|
62
|
-
},
|
|
63
|
-
ds
|
|
64
|
-
);
|
|
65
|
-
if (raw.error) throw raw.error;
|
|
66
|
-
const sampleList = Object.values(raw.samples);
|
|
67
|
-
let sites = sampleList.map((s) => {
|
|
68
|
-
const val = s[facilityTW.$id].value;
|
|
69
|
-
let label = facilityTW.term.values?.[val]?.label || val;
|
|
70
|
-
if (label.length > 50) label = label.slice(0, 47) + "...";
|
|
71
|
-
return { value: val, label };
|
|
72
|
-
});
|
|
73
|
-
if (userSites && query.filterByUserSites) {
|
|
74
|
-
sites = sites.filter((s) => userSites.includes(s.value));
|
|
75
|
-
}
|
|
76
|
-
sites.sort((a, b) => a.label.localeCompare(b.label));
|
|
77
|
-
const samples = Object.values(raw.samples);
|
|
78
|
-
const eligibleSamples = userSites && query.filterByUserSites ? samples.filter((s) => userSites.includes(s[facilityTW.$id].value)) : samples;
|
|
79
|
-
const term2Score = {};
|
|
80
|
-
for (const d of query.scoreTerms) {
|
|
81
|
-
const score = computeMedianPercentage(d, eligibleSamples);
|
|
82
|
-
if (score !== null) term2Score[d.score.term.id] = score;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
term2Score,
|
|
86
|
-
sites: isPublic ? [] : sites,
|
|
87
|
-
n: eligibleSamples.length
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
function computeMedianPercentage(d, samples) {
|
|
91
|
-
const percentages = [];
|
|
92
|
-
for (const s of samples) {
|
|
93
|
-
const scoreValue = s[d.score.$id]?.value;
|
|
94
|
-
if (scoreValue == null) continue;
|
|
95
|
-
let maxScoreValue = null;
|
|
96
|
-
if (typeof d.maxScore === "number") {
|
|
97
|
-
maxScoreValue = d.maxScore;
|
|
98
|
-
} else {
|
|
99
|
-
maxScoreValue = s[d.maxScore.$id]?.value;
|
|
100
|
-
}
|
|
101
|
-
if (maxScoreValue == null || maxScoreValue === 0) continue;
|
|
102
|
-
percentages.push(scoreValue / maxScoreValue * 100);
|
|
103
|
-
}
|
|
104
|
-
if (percentages.length === 0) return null;
|
|
105
|
-
percentages.sort((a, b) => a - b);
|
|
106
|
-
const mid = Math.floor(percentages.length / 2);
|
|
107
|
-
const median = percentages.length % 2 !== 0 ? percentages[mid] : (percentages[mid - 1] + percentages[mid]) / 2;
|
|
108
|
-
return Math.round(median);
|
|
109
|
-
}
|
|
110
|
-
export {
|
|
111
|
-
api
|
|
112
|
-
};
|