@sjcrh/proteinpaint-server 2.81.4-0 → 2.81.5
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 +3 -3
- package/routes/genesetOverrepresentation.js +1 -0
- package/routes/termdb.DE.js +4 -1
- package/routes/termdb.boxplot.js +123 -0
- package/routes/termdb.cluster.js +8 -3
- package/routes/termdb.config.js +3 -3
- package/routes/termdb.topVariablyExpressedGenes.js +2 -2
- package/src/app.js +1108 -791
- package/utils/edge.R +56 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.81.
|
|
3
|
+
"version": "2.81.5",
|
|
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",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@babel/preset-env": "^7.9.6",
|
|
44
44
|
"@babel/preset-typescript": "^7.21.4",
|
|
45
45
|
"@babel/register": "^7.14.5",
|
|
46
|
-
"@sjcrh/proteinpaint-types": "2.81.
|
|
46
|
+
"@sjcrh/proteinpaint-types": "2.81.5",
|
|
47
47
|
"@types/node": "^20.11.24",
|
|
48
48
|
"@types/tough-cookie": "^4.0.5",
|
|
49
49
|
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@sjcrh/augen": "2.46.0",
|
|
66
|
-
"@sjcrh/proteinpaint-rust": "2.
|
|
66
|
+
"@sjcrh/proteinpaint-rust": "2.81.5",
|
|
67
67
|
"@sjcrh/proteinpaint-shared": "2.79.4",
|
|
68
68
|
"better-sqlite3": "^9.4.1",
|
|
69
69
|
"body-parser": "^1.15.2",
|
|
@@ -37,6 +37,7 @@ async function run_genesetOverrepresentation_analysis(q, genomes) {
|
|
|
37
37
|
sample_genes: q.sample_genes,
|
|
38
38
|
msigdb: genomes[q.genome].termdbs.msigdb.cohort.db.connection.name,
|
|
39
39
|
gene_set_group: q.geneSetGroup,
|
|
40
|
+
filter_non_coding_genes: q.filter_non_coding_genes,
|
|
40
41
|
genedb: path.join(serverconfig.tpmasterdir, genomes[q.genome].genedb.dbfile)
|
|
41
42
|
};
|
|
42
43
|
if (q.background_genes) {
|
package/routes/termdb.DE.js
CHANGED
|
@@ -43,6 +43,7 @@ async function run_DE(param, ds) {
|
|
|
43
43
|
throw "samplelst.groups[0].values.length<1";
|
|
44
44
|
if (param.samplelst.groups[1].values?.length < 1)
|
|
45
45
|
throw "samplelst.groups[1].values.length<1";
|
|
46
|
+
param.storage_type = ds.queries.rnaseqGeneCount.storage_type;
|
|
46
47
|
const q = ds.queries.rnaseqGeneCount;
|
|
47
48
|
if (!q)
|
|
48
49
|
return;
|
|
@@ -83,9 +84,11 @@ async function run_DE(param, ds) {
|
|
|
83
84
|
const expression_input = {
|
|
84
85
|
case: cases_string,
|
|
85
86
|
control: controls_string,
|
|
87
|
+
data_type: "do_DE",
|
|
86
88
|
input_file: q.file,
|
|
87
89
|
min_count: param.min_count,
|
|
88
|
-
min_total_count: param.min_total_count
|
|
90
|
+
min_total_count: param.min_total_count,
|
|
91
|
+
storage_type: param.storage_type
|
|
89
92
|
};
|
|
90
93
|
const sample_size_limit = 8;
|
|
91
94
|
let result;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { getData } from "../src/termdb.matrix.js";
|
|
2
|
+
import { boxplot_getvalue } from "../src/utils.js";
|
|
3
|
+
import { sortKey2values } from "../src/termdb.violin.js";
|
|
4
|
+
const api = {
|
|
5
|
+
endpoint: "termdb/boxplot",
|
|
6
|
+
methods: {
|
|
7
|
+
all: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: "BoxplotRequest"
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: "BoxplotResponse"
|
|
14
|
+
},
|
|
15
|
+
examples: []
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
function init({ genomes }) {
|
|
20
|
+
return async (req, res) => {
|
|
21
|
+
const q = req.query;
|
|
22
|
+
try {
|
|
23
|
+
const genome = genomes[q.genome];
|
|
24
|
+
if (!genome)
|
|
25
|
+
throw "invalid genome name";
|
|
26
|
+
const ds = genome.datasets?.[q.dslabel];
|
|
27
|
+
if (!ds)
|
|
28
|
+
throw "invalid ds";
|
|
29
|
+
const terms = [q.tw];
|
|
30
|
+
if (q.divideTw)
|
|
31
|
+
terms.push(q.divideTw);
|
|
32
|
+
const data = await getData(
|
|
33
|
+
{
|
|
34
|
+
filter: q.filter,
|
|
35
|
+
filter0: q.filter0,
|
|
36
|
+
terms
|
|
37
|
+
},
|
|
38
|
+
ds,
|
|
39
|
+
genome
|
|
40
|
+
);
|
|
41
|
+
if (data.error)
|
|
42
|
+
throw data.error;
|
|
43
|
+
const sampleType = `All ${data.sampleType?.plural_name || "samples"}`;
|
|
44
|
+
const key2values = /* @__PURE__ */ new Map();
|
|
45
|
+
const overlayTerm = q.divideTw;
|
|
46
|
+
for (const [key, val] of Object.entries(data.samples)) {
|
|
47
|
+
const value = val[q.tw.$id];
|
|
48
|
+
if (!Number.isFinite(value?.value))
|
|
49
|
+
continue;
|
|
50
|
+
if (overlayTerm) {
|
|
51
|
+
if (!val[overlayTerm?.$id])
|
|
52
|
+
continue;
|
|
53
|
+
const value2 = val[overlayTerm.$id];
|
|
54
|
+
if (!key2values.has(value2.key))
|
|
55
|
+
key2values.set(value2.key, []);
|
|
56
|
+
key2values.get(value2.key).push(value.value);
|
|
57
|
+
} else {
|
|
58
|
+
if (!key2values.has(sampleType))
|
|
59
|
+
key2values.set(sampleType, []);
|
|
60
|
+
key2values.get(sampleType).push(value.value);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const plots = [];
|
|
64
|
+
let absMin, absMax, maxLabelLgth;
|
|
65
|
+
for (const [key, values] of sortKey2values(data, key2values, overlayTerm)) {
|
|
66
|
+
const sortedValues = values.sort((a, b) => a - b);
|
|
67
|
+
if (!absMin || sortedValues[0] < absMin)
|
|
68
|
+
absMin = sortedValues[0];
|
|
69
|
+
if (!absMax || sortedValues[sortedValues.length - 1] > absMax)
|
|
70
|
+
absMax = sortedValues[sortedValues.length - 1];
|
|
71
|
+
const vs = sortedValues.map((v) => {
|
|
72
|
+
const value = { value: v };
|
|
73
|
+
return value;
|
|
74
|
+
});
|
|
75
|
+
if (overlayTerm) {
|
|
76
|
+
let label = overlayTerm?.term?.values?.[key]?.label || key;
|
|
77
|
+
label = `${label}, n=${values.length}`;
|
|
78
|
+
if (!maxLabelLgth || label.length > maxLabelLgth.length)
|
|
79
|
+
maxLabelLgth = label.length;
|
|
80
|
+
const plot = {
|
|
81
|
+
// label,
|
|
82
|
+
// values,
|
|
83
|
+
seriesId: key,
|
|
84
|
+
color: overlayTerm?.term?.values?.[key]?.color || null,
|
|
85
|
+
boxplot: boxplot_getvalue(vs),
|
|
86
|
+
//Need sd and mean?
|
|
87
|
+
// plotValueCount: values.length,
|
|
88
|
+
min: sortedValues[0],
|
|
89
|
+
max: sortedValues[sortedValues.length - 1]
|
|
90
|
+
};
|
|
91
|
+
plot.boxplot.label = label;
|
|
92
|
+
plots.push(plot);
|
|
93
|
+
} else {
|
|
94
|
+
const label = `${sampleType}, n=${values.length}`;
|
|
95
|
+
if (!maxLabelLgth || label.length > maxLabelLgth.length)
|
|
96
|
+
maxLabelLgth = label.length;
|
|
97
|
+
const plot = {
|
|
98
|
+
// label,
|
|
99
|
+
// values,
|
|
100
|
+
// plotValueCount: values.length,
|
|
101
|
+
boxplot: boxplot_getvalue(vs),
|
|
102
|
+
min: sortedValues[0],
|
|
103
|
+
max: sortedValues[sortedValues.length - 1]
|
|
104
|
+
};
|
|
105
|
+
plot.boxplot.label = label;
|
|
106
|
+
plots.push(plot);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
data.plots = plots;
|
|
110
|
+
data.absMin = absMin;
|
|
111
|
+
data.absMax = absMax;
|
|
112
|
+
data.maxLabelLgth = maxLabelLgth;
|
|
113
|
+
res.send(data);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
res.send({ error: e?.message || e });
|
|
116
|
+
if (e instanceof Error && e.stack)
|
|
117
|
+
console.log(e);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
api
|
|
123
|
+
};
|
package/routes/termdb.cluster.js
CHANGED
|
@@ -72,18 +72,21 @@ async function getResult(q, ds) {
|
|
|
72
72
|
return { term: { gene: g, type: TermTypes.GENE_EXPRESSION }, data: term2sample2value.get(g) };
|
|
73
73
|
}
|
|
74
74
|
const t = Date.now();
|
|
75
|
-
const clustering = await doClustering(term2sample2value, q);
|
|
75
|
+
const clustering = await doClustering(term2sample2value, q, Object.keys(bySampleId).length);
|
|
76
76
|
if (serverconfig.debugmode)
|
|
77
77
|
console.log("clustering done:", Date.now() - t, "ms");
|
|
78
78
|
return { clustering, byTermId, bySampleId };
|
|
79
79
|
}
|
|
80
|
-
async function doClustering(data, q) {
|
|
80
|
+
async function doClustering(data, q, numCases = 1e3) {
|
|
81
81
|
const sampleSet = /* @__PURE__ */ new Set();
|
|
82
82
|
for (const o of data.values()) {
|
|
83
83
|
for (const s in o)
|
|
84
84
|
sampleSet.add(s);
|
|
85
|
-
|
|
85
|
+
if (sampleSet.size >= numCases)
|
|
86
|
+
break;
|
|
86
87
|
}
|
|
88
|
+
if (sampleSet.size == 0)
|
|
89
|
+
throw "termdb.cluster: no samples";
|
|
87
90
|
if (!clusterMethodLst.find((i) => i.value == q.clusterMethod))
|
|
88
91
|
throw "Invalid cluster method";
|
|
89
92
|
if (!distanceMethodLst.find((i) => i.value == q.distanceMethod))
|
|
@@ -107,6 +110,8 @@ async function doClustering(data, q) {
|
|
|
107
110
|
}
|
|
108
111
|
inputData.matrix.push(getZscore(row));
|
|
109
112
|
}
|
|
113
|
+
if (inputData.matrix.length == 0)
|
|
114
|
+
throw "Clustering matrix is empty";
|
|
110
115
|
const Routput = JSON.parse(
|
|
111
116
|
await run_R(path.join(serverconfig.binpath, "utils", "hclust.R"), JSON.stringify(inputData))
|
|
112
117
|
);
|
package/routes/termdb.config.js
CHANGED
|
@@ -64,8 +64,8 @@ function make(q, res, ds, genome) {
|
|
|
64
64
|
hasSampleAncestry: ds.cohort.termdb.hasSampleAncestry,
|
|
65
65
|
defaultChartType: ds.cohort.defaultChartType
|
|
66
66
|
};
|
|
67
|
-
if (tdb.
|
|
68
|
-
c.
|
|
67
|
+
if (tdb.plotConfigByCohort)
|
|
68
|
+
c.plotConfigByCohort = tdb.plotConfigByCohort;
|
|
69
69
|
if (tdb.multipleTestingCorrection)
|
|
70
70
|
c.multipleTestingCorrection = tdb.multipleTestingCorrection;
|
|
71
71
|
if (tdb.neuroOncRegression)
|
|
@@ -222,7 +222,7 @@ function addNonDictionaryQueries(c, ds, genome) {
|
|
|
222
222
|
width: q.singleCell.data.width,
|
|
223
223
|
height: q.singleCell.data.height,
|
|
224
224
|
plots: q.singleCell.data.plots.map((p) => {
|
|
225
|
-
return { name: p.name, colorColumns: p.colorColumns.map((c2) => c2.name) };
|
|
225
|
+
return { name: p.name, colorColumns: p.colorColumns.map((c2) => c2.name), selected: p.selected };
|
|
226
226
|
})
|
|
227
227
|
}
|
|
228
228
|
};
|
|
@@ -3,6 +3,7 @@ import serverconfig from "#src/serverconfig.js";
|
|
|
3
3
|
import { get_samples } from "#src/termdb.sql.js";
|
|
4
4
|
import { makeFilter } from "#src/mds3.gdc.js";
|
|
5
5
|
import { cachedFetch } from "#src/utils.js";
|
|
6
|
+
import { joinUrl } from "#src/helpers.ts";
|
|
6
7
|
const api = {
|
|
7
8
|
endpoint: "termdb/topVariablyExpressedGenes",
|
|
8
9
|
methods: {
|
|
@@ -181,10 +182,9 @@ function gdcValidateQuery(ds, genome) {
|
|
|
181
182
|
throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
|
|
182
183
|
}
|
|
183
184
|
const { host, headers } = ds.getHostHeaders(q);
|
|
184
|
-
const url = `${host.geneExp}/gene_expression/gene_selection`;
|
|
185
185
|
try {
|
|
186
186
|
const response = await cachedFetch(
|
|
187
|
-
|
|
187
|
+
joinUrl(host.geneExp, "/gene_expression/gene_selection"),
|
|
188
188
|
{
|
|
189
189
|
method: "POST",
|
|
190
190
|
headers,
|