@sjcrh/proteinpaint-server 2.98.0 → 2.98.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/package.json +2 -2
- package/routes/correlationVolcano.js +128 -0
- package/routes/termdb.config.js +21 -8
- package/routes/termdb.violin.js +7 -10
- package/src/app.js +1592 -911
- package/src/serverconfig.js +9 -1
- package/utils/corr.R +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.98.
|
|
3
|
+
"version": "2.98.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",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@sjcrh/augen": "2.87.0",
|
|
62
62
|
"@sjcrh/proteinpaint-rust": "2.84.0",
|
|
63
63
|
"@sjcrh/proteinpaint-shared": "2.98.0",
|
|
64
|
-
"@sjcrh/proteinpaint-types": "2.98.
|
|
64
|
+
"@sjcrh/proteinpaint-types": "2.98.1",
|
|
65
65
|
"better-sqlite3": "^9.4.1",
|
|
66
66
|
"body-parser": "^1.15.2",
|
|
67
67
|
"canvas": "~2.11.2",
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { CorrelationVolcanoPayload } from "#types/checkers";
|
|
2
|
+
import { getData } from "../src/termdb.matrix.js";
|
|
3
|
+
import run_R from "../src/run_R.js";
|
|
4
|
+
import serverconfig from "../src/serverconfig.js";
|
|
5
|
+
import { mayLog } from "#src/helpers.ts";
|
|
6
|
+
import path from "path";
|
|
7
|
+
const api = {
|
|
8
|
+
endpoint: "termdb/correlationVolcano",
|
|
9
|
+
methods: {
|
|
10
|
+
get: {
|
|
11
|
+
...CorrelationVolcanoPayload,
|
|
12
|
+
init
|
|
13
|
+
},
|
|
14
|
+
post: {
|
|
15
|
+
...CorrelationVolcanoPayload,
|
|
16
|
+
init
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function init({ genomes }) {
|
|
21
|
+
return async (req, res) => {
|
|
22
|
+
const q = req.query;
|
|
23
|
+
try {
|
|
24
|
+
const genome = genomes[q.genome];
|
|
25
|
+
if (!genome)
|
|
26
|
+
throw "invalid genome name";
|
|
27
|
+
const ds = genome.datasets?.[q.dslabel];
|
|
28
|
+
if (!ds)
|
|
29
|
+
throw "invalid ds";
|
|
30
|
+
const result = await compute(q, ds, genome);
|
|
31
|
+
res.send(result);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
res.send({ error: e?.message || e });
|
|
34
|
+
if (e instanceof Error && e.stack)
|
|
35
|
+
console.error(e);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function compute(q, ds, genome) {
|
|
40
|
+
const terms = [q.featureTw, ...q.variableTwLst];
|
|
41
|
+
const data = await getData(
|
|
42
|
+
{
|
|
43
|
+
filter: q.filter,
|
|
44
|
+
filter0: q.filter0,
|
|
45
|
+
terms
|
|
46
|
+
},
|
|
47
|
+
ds,
|
|
48
|
+
genome
|
|
49
|
+
);
|
|
50
|
+
if (data.error)
|
|
51
|
+
throw data.error;
|
|
52
|
+
const vtid2array = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const tw of q.variableTwLst) {
|
|
54
|
+
vtid2array.set(tw.$id, { id: tw.$id, v1: [], v2: [] });
|
|
55
|
+
}
|
|
56
|
+
for (const sid in data.samples) {
|
|
57
|
+
const featureValue = data.samples[sid][q.featureTw.$id]?.value;
|
|
58
|
+
if (!Number.isFinite(featureValue))
|
|
59
|
+
continue;
|
|
60
|
+
for (const tw of q.variableTwLst) {
|
|
61
|
+
const variableValue = data.samples[sid][tw.$id]?.value;
|
|
62
|
+
if (!Number.isFinite(variableValue))
|
|
63
|
+
continue;
|
|
64
|
+
vtid2array.get(tw.$id).v1.push(featureValue);
|
|
65
|
+
vtid2array.get(tw.$id).v2.push(variableValue);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const input = {
|
|
69
|
+
method: q.correlationMethod || "pearson",
|
|
70
|
+
terms: [...vtid2array.values()]
|
|
71
|
+
};
|
|
72
|
+
const time1 = Date.now();
|
|
73
|
+
const r_output = await run_R(path.join(serverconfig.binpath, "utils", "corr.R"), JSON.stringify(input));
|
|
74
|
+
mayLog("Time taken to run correlation analysis:", Date.now() - time1);
|
|
75
|
+
let json_result;
|
|
76
|
+
for (const line of r_output.split("\n")) {
|
|
77
|
+
if (line.startsWith("adjusted_p_values:")) {
|
|
78
|
+
json_result = JSON.parse(line.replace("adjusted_p_values:", ""));
|
|
79
|
+
} else {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const output = { terms: json_result };
|
|
83
|
+
const result = { variableItems: [] };
|
|
84
|
+
for (const t of output.terms) {
|
|
85
|
+
const t2 = {
|
|
86
|
+
tw$id: t.id,
|
|
87
|
+
sampleSize: t.sample_size,
|
|
88
|
+
//sampleSize: input.terms.get(t.id).v1.length, // This was not working so passed the length of each array from R
|
|
89
|
+
correlation: t.correlation,
|
|
90
|
+
original_pvalue: t.original_p_value,
|
|
91
|
+
adjusted_pvalue: t.adjusted_p_value
|
|
92
|
+
};
|
|
93
|
+
result.variableItems.push(t2);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function validate_correlationVolcano(ds) {
|
|
98
|
+
const cv = ds.cohort.correlationVolcano;
|
|
99
|
+
if (!cv)
|
|
100
|
+
return;
|
|
101
|
+
if (typeof cv.feature != "object")
|
|
102
|
+
throw "cv.feature not obj";
|
|
103
|
+
if (cv.feature.termType == "geneExpression") {
|
|
104
|
+
if (!ds.queries?.geneExpression)
|
|
105
|
+
throw "cv.feature.termType=geneExpression not supported by ds";
|
|
106
|
+
} else {
|
|
107
|
+
throw "unknown cv.feature.termType";
|
|
108
|
+
}
|
|
109
|
+
if (typeof cv.variables != "object")
|
|
110
|
+
throw "cv.variables not obj";
|
|
111
|
+
if (cv.variables.type == "dictionaryTerm") {
|
|
112
|
+
if (!Array.isArray(cv.variables.termIds))
|
|
113
|
+
throw "cv.variables.termIds not array when type=dictionaryTerm";
|
|
114
|
+
for (const id of cv.variables.termIds) {
|
|
115
|
+
const t = ds.cohort.termdb.q.termjsonByOneid(id);
|
|
116
|
+
if (!t)
|
|
117
|
+
throw "cv.variables.termIds: unknown id: " + id;
|
|
118
|
+
if (t.type != "integer" && t.type != "float")
|
|
119
|
+
throw "cv.variables.termIds: not integer/float: " + id;
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
throw "unknown cv.variables.type";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
api,
|
|
127
|
+
validate_correlationVolcano
|
|
128
|
+
};
|
package/routes/termdb.config.js
CHANGED
|
@@ -27,7 +27,7 @@ function init({ genomes }) {
|
|
|
27
27
|
if (!genome)
|
|
28
28
|
throw "invalid genome";
|
|
29
29
|
const [ds] = get_ds_tdb(genome, q);
|
|
30
|
-
return make(q, res, ds, genome);
|
|
30
|
+
return make(q, req, res, ds, genome);
|
|
31
31
|
} catch (e) {
|
|
32
32
|
res.send({ error: e.message || e });
|
|
33
33
|
if (e.stack)
|
|
@@ -37,13 +37,11 @@ function init({ genomes }) {
|
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
function make(q, res, ds, genome) {
|
|
40
|
+
function make(q, req, res, ds, genome) {
|
|
41
41
|
const tdb = ds.cohort.termdb;
|
|
42
|
-
const auth = { embedder: q.embedder };
|
|
43
42
|
const c = {
|
|
44
|
-
selectCohort:
|
|
45
|
-
|
|
46
|
-
supportedChartTypes: tdb.q?.getSupportedChartTypes(auth),
|
|
43
|
+
selectCohort: getSelectCohort(ds, req),
|
|
44
|
+
supportedChartTypes: tdb.q?.getSupportedChartTypes(req),
|
|
47
45
|
renamedChartTypes: ds.cohort.renamedChartTypes,
|
|
48
46
|
allowedTermTypes: getAllowedTermTypes(ds),
|
|
49
47
|
termMatch2geneSet: tdb.termMatch2geneSet,
|
|
@@ -68,8 +66,6 @@ function make(q, res, ds, genome) {
|
|
|
68
66
|
c.plotConfigByCohort = tdb.plotConfigByCohort;
|
|
69
67
|
if (tdb.multipleTestingCorrection)
|
|
70
68
|
c.multipleTestingCorrection = tdb.multipleTestingCorrection;
|
|
71
|
-
if (tdb.neuroOncRegression)
|
|
72
|
-
c.neuroOncRegression = tdb.neuroOncRegression;
|
|
73
69
|
if (tdb.helpPages)
|
|
74
70
|
c.helpPages = tdb.helpPages;
|
|
75
71
|
if (tdb.minTimeSinceDx)
|
|
@@ -90,15 +86,22 @@ function make(q, res, ds, genome) {
|
|
|
90
86
|
c.excludedTermtypeByTarget = tdb.excludedTermtypeByTarget;
|
|
91
87
|
if (tdb.survival)
|
|
92
88
|
c.survival = tdb.survival;
|
|
89
|
+
if (tdb.regression)
|
|
90
|
+
c.regression = tdb.regression;
|
|
93
91
|
if (ds.assayAvailability)
|
|
94
92
|
c.assayAvailability = ds.assayAvailability;
|
|
95
93
|
if (ds.customTwQByType)
|
|
96
94
|
c.customTwQByType = ds.customTwQByType;
|
|
95
|
+
if (ds.cohort.correlationVolcano)
|
|
96
|
+
c.correlationVolcano = ds.cohort.correlationVolcano;
|
|
97
97
|
c.requiredAuth = authApi.getRequiredCredForDsEmbedder(q.dslabel, q.embedder);
|
|
98
98
|
addRestrictAncestries(c, tdb);
|
|
99
99
|
addScatterplots(c, ds);
|
|
100
100
|
addMatrixplots(c, ds);
|
|
101
101
|
addNonDictionaryQueries(c, ds, genome);
|
|
102
|
+
c.requiredAuth = authApi.getRequiredCredForDsEmbedder(q.dslabel, q.embedder);
|
|
103
|
+
const info = authApi.getNonsensitiveInfo(req);
|
|
104
|
+
c.clientAuthResult = info.clientAuthResult || {};
|
|
102
105
|
res.send({ termdbConfig: c });
|
|
103
106
|
}
|
|
104
107
|
function addRestrictAncestries(c, tdb) {
|
|
@@ -256,6 +259,16 @@ function getAllowedTermTypes(ds) {
|
|
|
256
259
|
typeSet.add(TermTypes.METABOLITE_INTENSITY);
|
|
257
260
|
return [...typeSet];
|
|
258
261
|
}
|
|
262
|
+
function getSelectCohort(ds, req) {
|
|
263
|
+
if (!ds.cohort.termdb.selectCohort)
|
|
264
|
+
return;
|
|
265
|
+
const copy = Object.assign({}, ds.cohort.termdb.selectCohort);
|
|
266
|
+
if (ds.cohort.termdb.selectCohort.descriptionByUser) {
|
|
267
|
+
copy.description = ds.cohort.termdb.selectCohort.descriptionByUser(authApi.getNonsensitiveInfo(req));
|
|
268
|
+
delete copy.descriptionByUser;
|
|
269
|
+
}
|
|
270
|
+
return copy;
|
|
271
|
+
}
|
|
259
272
|
export {
|
|
260
273
|
api
|
|
261
274
|
};
|
package/routes/termdb.violin.js
CHANGED
|
@@ -122,7 +122,7 @@ function divideValues(q, data, sampleType) {
|
|
|
122
122
|
key2values,
|
|
123
123
|
min: absMin,
|
|
124
124
|
max: absMax,
|
|
125
|
-
|
|
125
|
+
uncomputableValues: sortObj(uncomputableValues)
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
function sortObj(object) {
|
|
@@ -148,16 +148,14 @@ function setResponse(valuesObject, data, q, sampleType) {
|
|
|
148
148
|
seriesId: key,
|
|
149
149
|
plotValueCount: values?.length,
|
|
150
150
|
color: overlayTerm?.term?.values?.[key]?.color || null,
|
|
151
|
-
divideTwBins: isNumericTerm(overlayTerm.term) ? numericBins(overlayTerm, data) : null
|
|
152
|
-
uncomputableValueObj: Object.keys(valuesObject.uncomputableValueObj).length > 0 ? valuesObject.uncomputableValueObj : null
|
|
151
|
+
divideTwBins: isNumericTerm(overlayTerm.term) ? numericBins(overlayTerm, data) : null
|
|
153
152
|
});
|
|
154
153
|
} else {
|
|
155
|
-
|
|
154
|
+
plots.push({
|
|
156
155
|
label: sampleType,
|
|
157
156
|
values,
|
|
158
157
|
plotValueCount: values.length
|
|
159
|
-
};
|
|
160
|
-
plots.push(plot);
|
|
158
|
+
});
|
|
161
159
|
}
|
|
162
160
|
}
|
|
163
161
|
const result = {
|
|
@@ -165,7 +163,7 @@ function setResponse(valuesObject, data, q, sampleType) {
|
|
|
165
163
|
max: valuesObject.max,
|
|
166
164
|
plots,
|
|
167
165
|
pvalues: [],
|
|
168
|
-
|
|
166
|
+
uncomputableValues: Object.keys(valuesObject.uncomputableValues).length > 0 ? valuesObject.uncomputableValues : null
|
|
169
167
|
};
|
|
170
168
|
return result;
|
|
171
169
|
}
|
|
@@ -222,9 +220,8 @@ function createCanvasImg(q, result, ds) {
|
|
|
222
220
|
ctx.stroke();
|
|
223
221
|
});
|
|
224
222
|
plot.src = canvas.toDataURL();
|
|
225
|
-
|
|
226
|
-
plot.
|
|
227
|
-
plot.summaryStats = summaryStats(plot.values);
|
|
223
|
+
plot.density = getBinsDensity(axisScale, plot, q.isKDE, q.ticks);
|
|
224
|
+
plot.summaryStats = summaryStats(plot.values).values;
|
|
228
225
|
delete plot.values;
|
|
229
226
|
}
|
|
230
227
|
}
|