@sjcrh/proteinpaint-server 2.142.0 → 2.143.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/dataset/protected.test.js +1 -2
- package/dataset/termdb.test.js +3 -2
- package/package.json +9 -8
- package/routes/aiProjectAdmin.js +46 -60
- package/routes/aiProjectSelectedWSImages.js +161 -0
- package/routes/brainImaging.js +9 -18
- package/routes/brainImagingSamples.js +2 -4
- package/routes/burden.js +13 -26
- package/routes/correlationVolcano.js +18 -36
- package/routes/dataset.js +6 -12
- package/routes/deleteWSIAnnotation.js +75 -0
- package/routes/dsdata.js +7 -14
- package/routes/dzimages.js +4 -8
- package/routes/gdc.grin2.list.js +13 -26
- package/routes/gdc.grin2.run.js +3 -6
- package/routes/gdc.maf.js +8 -16
- package/routes/gdc.mafBuild.js +14 -28
- package/routes/gene2canonicalisoform.js +4 -8
- package/routes/genelookup.js +2 -4
- package/routes/genesetEnrichment.js +6 -12
- package/routes/genesetOverrepresentation.js +1 -2
- package/routes/genomes.js +1 -2
- package/routes/grin2.js +13 -17
- package/routes/healthcheck.js +3 -6
- package/routes/hicdata.js +4 -8
- package/routes/hicgenome.js +4 -8
- package/routes/hicstat.js +2 -4
- package/routes/img.js +1 -2
- package/routes/isoformlst.js +6 -12
- package/routes/ntseq.js +4 -8
- package/routes/pdomain.js +5 -10
- package/routes/sampledzimages.js +2 -4
- package/routes/samplewsimages.js +3 -67
- package/routes/saveWSIAnnotation.js +100 -0
- package/routes/snp.js +9 -18
- package/routes/termdb.DE.js +23 -46
- package/routes/termdb.boxplot.js +84 -84
- package/routes/termdb.categories.js +9 -18
- package/routes/termdb.cluster.js +23 -46
- package/routes/termdb.cohort.summary.js +3 -6
- package/routes/termdb.cohorts.js +4 -8
- package/routes/termdb.config.js +32 -64
- package/routes/termdb.descrstats.js +6 -12
- package/routes/termdb.filterTermValues.js +4 -8
- package/routes/termdb.numericcategories.js +5 -10
- package/routes/termdb.percentile.js +6 -12
- package/routes/termdb.profileFormScores.js +12 -24
- package/routes/termdb.profileScores.js +7 -14
- package/routes/termdb.rootterm.js +4 -8
- package/routes/termdb.sampleImages.js +4 -8
- package/routes/termdb.singleSampleMutation.js +9 -18
- package/routes/termdb.singlecellDEgenes.js +4 -8
- package/routes/termdb.singlecellData.js +4 -8
- package/routes/termdb.singlecellSamples.js +28 -56
- package/routes/termdb.termchildren.js +5 -10
- package/routes/termdb.termsbyids.js +4 -8
- package/routes/termdb.topMutatedGenes.js +15 -30
- package/routes/termdb.topTermsByType.js +9 -18
- package/routes/termdb.topVariablyExpressedGenes.js +13 -26
- package/routes/termdb.violin.js +124 -135
- package/routes/tileserver.js +14 -15
- package/routes/wsimages.js +42 -46
- package/routes/wsisamples.js +3 -6
- package/src/app.js +4345 -6708
- package/routes/sampleWsiAiApi.js +0 -33
package/routes/termdb.DE.js
CHANGED
|
@@ -28,8 +28,7 @@ function init({ genomes }) {
|
|
|
28
28
|
try {
|
|
29
29
|
const q = req.query;
|
|
30
30
|
const genome = genomes[q.genome];
|
|
31
|
-
if (!genome)
|
|
32
|
-
throw "invalid genome";
|
|
31
|
+
if (!genome) throw "invalid genome";
|
|
33
32
|
const [ds] = get_ds_tdb(genome, q);
|
|
34
33
|
let term_results = [];
|
|
35
34
|
if (q.tw) {
|
|
@@ -42,8 +41,7 @@ function init({ genomes }) {
|
|
|
42
41
|
},
|
|
43
42
|
ds
|
|
44
43
|
);
|
|
45
|
-
if (term_results.error)
|
|
46
|
-
throw term_results.error;
|
|
44
|
+
if (term_results.error) throw term_results.error;
|
|
47
45
|
}
|
|
48
46
|
let term_results2 = [];
|
|
49
47
|
if (q.tw2) {
|
|
@@ -56,44 +54,33 @@ function init({ genomes }) {
|
|
|
56
54
|
},
|
|
57
55
|
ds
|
|
58
56
|
);
|
|
59
|
-
if (term_results2.error)
|
|
60
|
-
throw term_results2.error;
|
|
57
|
+
if (term_results2.error) throw term_results2.error;
|
|
61
58
|
}
|
|
62
59
|
const results = await run_DE(req.query, ds, term_results, term_results2);
|
|
63
|
-
if (!results || !results.data)
|
|
64
|
-
throw "No data [termdb.DE.ts init()]";
|
|
60
|
+
if (!results || !results.data) throw "No data [termdb.DE.ts init()]";
|
|
65
61
|
res.send(results);
|
|
66
62
|
} catch (e) {
|
|
67
63
|
res.send({ status: "error", error: e.message || e });
|
|
68
|
-
if (e instanceof Error && e.stack)
|
|
69
|
-
console.log(e);
|
|
64
|
+
if (e instanceof Error && e.stack) console.log(e);
|
|
70
65
|
}
|
|
71
66
|
};
|
|
72
67
|
}
|
|
73
68
|
async function run_DE(param, ds, term_results, term_results2) {
|
|
74
|
-
if (param.samplelst?.groups?.length != 2)
|
|
75
|
-
|
|
76
|
-
if (param.samplelst.groups[
|
|
77
|
-
throw "samplelst.groups[0].values.length<1";
|
|
78
|
-
if (param.samplelst.groups[1].values?.length < 1)
|
|
79
|
-
throw "samplelst.groups[1].values.length<1";
|
|
69
|
+
if (param.samplelst?.groups?.length != 2) throw ".samplelst.groups.length!=2";
|
|
70
|
+
if (param.samplelst.groups[0].values?.length < 1) throw "samplelst.groups[0].values.length<1";
|
|
71
|
+
if (param.samplelst.groups[1].values?.length < 1) throw "samplelst.groups[1].values.length<1";
|
|
80
72
|
const q = ds.queries.rnaseqGeneCount;
|
|
81
|
-
if (!q)
|
|
82
|
-
|
|
83
|
-
if (!q.
|
|
84
|
-
throw "unknown data type for rnaseqGeneCount";
|
|
85
|
-
if (!q.storage_type)
|
|
86
|
-
throw "storage_type is not defined";
|
|
73
|
+
if (!q) return;
|
|
74
|
+
if (!q.file) throw "unknown data type for rnaseqGeneCount";
|
|
75
|
+
if (!q.storage_type) throw "storage_type is not defined";
|
|
87
76
|
param.storage_type = q.storage_type;
|
|
88
77
|
const group1names = [];
|
|
89
78
|
const conf1_group1 = [];
|
|
90
79
|
const conf2_group1 = [];
|
|
91
80
|
for (const s of param.samplelst.groups[0].values) {
|
|
92
|
-
if (!Number.isInteger(s.sampleId))
|
|
93
|
-
continue;
|
|
81
|
+
if (!Number.isInteger(s.sampleId)) continue;
|
|
94
82
|
const n = ds.cohort.termdb.q.id2sampleName(s.sampleId);
|
|
95
|
-
if (!n)
|
|
96
|
-
continue;
|
|
83
|
+
if (!n) continue;
|
|
97
84
|
if (q.allSampleSet.has(n)) {
|
|
98
85
|
if (param.tw && !param.tw2) {
|
|
99
86
|
if (term_results.samples[s.sampleId]) {
|
|
@@ -136,11 +123,9 @@ async function run_DE(param, ds, term_results, term_results2) {
|
|
|
136
123
|
const conf1_group2 = [];
|
|
137
124
|
const conf2_group2 = [];
|
|
138
125
|
for (const s of param.samplelst.groups[1].values) {
|
|
139
|
-
if (!Number.isInteger(s.sampleId))
|
|
140
|
-
continue;
|
|
126
|
+
if (!Number.isInteger(s.sampleId)) continue;
|
|
141
127
|
const n = ds.cohort.termdb.q.id2sampleName(s.sampleId);
|
|
142
|
-
if (!n)
|
|
143
|
-
continue;
|
|
128
|
+
if (!n) continue;
|
|
144
129
|
if (q.allSampleSet.has(n)) {
|
|
145
130
|
if (param.tw && !param.tw2) {
|
|
146
131
|
if (term_results.samples[s.sampleId]) {
|
|
@@ -181,10 +166,8 @@ async function run_DE(param, ds, term_results, term_results2) {
|
|
|
181
166
|
}
|
|
182
167
|
const sample_size1 = group1names.length;
|
|
183
168
|
const sample_size2 = group2names.length;
|
|
184
|
-
if (sample_size1 < 1)
|
|
185
|
-
|
|
186
|
-
if (sample_size2 < 1)
|
|
187
|
-
throw "sample size of group2 < 1";
|
|
169
|
+
if (sample_size1 < 1) throw "sample size of group1 < 1";
|
|
170
|
+
if (sample_size2 < 1) throw "sample size of group2 < 1";
|
|
188
171
|
const commonnames = group1names.filter((element) => group2names.includes(element));
|
|
189
172
|
if (commonnames.length > 0) {
|
|
190
173
|
throw "Common elements found between both groups:" + commonnames.map((i) => i).join(",");
|
|
@@ -233,8 +216,7 @@ async function run_DE(param, ds, term_results, term_results2) {
|
|
|
233
216
|
await readFileAndDelete(mds_imagePath, "mds_image", result2);
|
|
234
217
|
}
|
|
235
218
|
const images = [result2.ql_image];
|
|
236
|
-
if (result2.mds_image)
|
|
237
|
-
images.push(result2.mds_image);
|
|
219
|
+
if (result2.mds_image) images.push(result2.mds_image);
|
|
238
220
|
return {
|
|
239
221
|
data: result2.gene_data,
|
|
240
222
|
sample_size1,
|
|
@@ -259,16 +241,13 @@ async function readFileAndDelete(file, key, response) {
|
|
|
259
241
|
};
|
|
260
242
|
response[key] = obj;
|
|
261
243
|
fs.unlink(file, (err) => {
|
|
262
|
-
if (err)
|
|
263
|
-
throw err;
|
|
244
|
+
if (err) throw err;
|
|
264
245
|
});
|
|
265
246
|
}
|
|
266
247
|
async function validate_query_rnaseqGeneCount(ds) {
|
|
267
248
|
const q = ds.queries.rnaseqGeneCount;
|
|
268
|
-
if (!q)
|
|
269
|
-
|
|
270
|
-
if (!q.file)
|
|
271
|
-
throw "unknown data type for rnaseqGeneCount";
|
|
249
|
+
if (!q) return;
|
|
250
|
+
if (!q.file) throw "unknown data type for rnaseqGeneCount";
|
|
272
251
|
q.file = path.join(serverconfig.tpmasterdir, q.file);
|
|
273
252
|
{
|
|
274
253
|
let samples = [];
|
|
@@ -284,13 +263,11 @@ async function validate_query_rnaseqGeneCount(ds) {
|
|
|
284
263
|
const time2 = (/* @__PURE__ */ new Date()).valueOf();
|
|
285
264
|
mayLog("Time taken to query gene expression:", time2 - time1, "ms");
|
|
286
265
|
samples = result.split(",");
|
|
287
|
-
} else
|
|
288
|
-
throw "unknown storage type:" + ds.queries.rnaseqGeneCount.storage_type;
|
|
266
|
+
} else throw "unknown storage type:" + ds.queries.rnaseqGeneCount.storage_type;
|
|
289
267
|
q.allSampleSet = new Set(samples);
|
|
290
268
|
const unknownSamples = [];
|
|
291
269
|
for (const n of q.allSampleSet) {
|
|
292
|
-
if (!ds.cohort.termdb.q.sampleName2id(n))
|
|
293
|
-
unknownSamples.push(n);
|
|
270
|
+
if (!ds.cohort.termdb.q.sampleName2id(n)) unknownSamples.push(n);
|
|
294
271
|
}
|
|
295
272
|
console.log(q.allSampleSet.size, `rnaseqGeneCount samples from ${ds.label}`);
|
|
296
273
|
}
|
package/routes/termdb.boxplot.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { boxplotPayload } from "#types/checkers";
|
|
2
2
|
import { getData } from "../src/termdb.matrix.js";
|
|
3
3
|
import { boxplot_getvalue } from "../src/utils.js";
|
|
4
|
-
import {
|
|
4
|
+
import { sortPlot2Values } from "./termdb.violin.ts";
|
|
5
5
|
import { roundValueAuto } from "#shared/roundValue.js";
|
|
6
6
|
import { getMean, getVariance } from "#shared/descriptive.stats.js";
|
|
7
7
|
const minSampleSize = 5;
|
|
@@ -23,103 +23,99 @@ function init({ genomes }) {
|
|
|
23
23
|
const q = req.query;
|
|
24
24
|
try {
|
|
25
25
|
const genome = genomes[q.genome];
|
|
26
|
-
if (!genome)
|
|
27
|
-
throw "invalid genome name";
|
|
26
|
+
if (!genome) throw "invalid genome name";
|
|
28
27
|
const ds = genome.datasets?.[q.dslabel];
|
|
29
|
-
if (!ds)
|
|
30
|
-
throw "invalid ds";
|
|
28
|
+
if (!ds) throw "invalid ds";
|
|
31
29
|
const terms = [q.tw];
|
|
32
|
-
if (q.overlayTw)
|
|
33
|
-
|
|
30
|
+
if (q.overlayTw) terms.push(q.overlayTw);
|
|
31
|
+
if (q.divideTw) terms.push(q.divideTw);
|
|
34
32
|
const data = await getData({ filter: q.filter, filter0: q.filter0, terms, __protected__: q.__protected__ }, ds);
|
|
35
|
-
if (data.error)
|
|
36
|
-
throw data.error;
|
|
33
|
+
if (data.error) throw data.error;
|
|
37
34
|
const sampleType = `All ${data.sampleType?.plural_name || "samples"}`;
|
|
38
35
|
const overlayTerm = q.overlayTw;
|
|
39
|
-
const
|
|
36
|
+
const divideTerm = q.divideTw;
|
|
37
|
+
const { absMin, absMax, chart2plot2values, uncomputableValues } = parseValues(
|
|
40
38
|
q,
|
|
41
39
|
data,
|
|
42
40
|
sampleType,
|
|
43
41
|
q.isLogScale,
|
|
44
|
-
overlayTerm
|
|
42
|
+
overlayTerm,
|
|
43
|
+
divideTerm
|
|
45
44
|
);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const descrStats = setDescrStats(boxplot, sortedValues);
|
|
57
|
-
const _plot = {
|
|
58
|
-
boxplot,
|
|
59
|
-
descrStats
|
|
60
|
-
};
|
|
61
|
-
if (overlayTerm) {
|
|
62
|
-
const _key = overlayTerm?.term?.values?.[key]?.label || key;
|
|
63
|
-
const plotLabel = `${_key}, n=${values.length}`;
|
|
64
|
-
const overlayBins = numericBins(overlayTerm, data);
|
|
65
|
-
const plot = Object.assign(_plot, {
|
|
66
|
-
color: overlayTerm?.term?.values?.[key]?.color || null,
|
|
67
|
-
key: _key,
|
|
68
|
-
overlayBins: overlayBins.has(key) ? overlayBins.get(key) : null,
|
|
69
|
-
seriesId: key
|
|
45
|
+
if (!absMin && absMin !== 0) throw "absMin is undefined [termdb.boxplot init()]";
|
|
46
|
+
if (!absMax && absMax !== 0) throw "absMax is undefined [termdb.boxplot init()]";
|
|
47
|
+
const charts = {};
|
|
48
|
+
for (const [chart, plot2values] of chart2plot2values) {
|
|
49
|
+
const plots = [];
|
|
50
|
+
for (const [key, values] of sortPlot2Values(data, plot2values, overlayTerm)) {
|
|
51
|
+
const sortedValues = values.sort((a, b) => a - b);
|
|
52
|
+
const vs = sortedValues.map((v) => {
|
|
53
|
+
const value = { value: v };
|
|
54
|
+
return value;
|
|
70
55
|
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
56
|
+
const boxplot = boxplot_getvalue(vs);
|
|
57
|
+
if (!boxplot) throw "boxplot_getvalue failed [termdb.boxplot init()]";
|
|
58
|
+
const descrStats = setDescrStats(boxplot, sortedValues);
|
|
59
|
+
const _plot = {
|
|
60
|
+
boxplot,
|
|
61
|
+
descrStats
|
|
62
|
+
};
|
|
63
|
+
if (overlayTerm) {
|
|
64
|
+
const _key = overlayTerm?.term?.values?.[key]?.label || key;
|
|
65
|
+
const plotLabel = `${_key}, n=${values.length}`;
|
|
66
|
+
const overlayBins = numericBins(overlayTerm, data);
|
|
67
|
+
const plot = Object.assign(_plot, {
|
|
68
|
+
color: overlayTerm?.term?.values?.[key]?.color || null,
|
|
69
|
+
key: _key,
|
|
70
|
+
overlayBins: overlayBins.has(key) ? overlayBins.get(key) : null,
|
|
71
|
+
seriesId: key
|
|
72
|
+
});
|
|
73
|
+
plot.boxplot.label = plotLabel;
|
|
74
|
+
plots.push(plot);
|
|
75
|
+
} else {
|
|
76
|
+
const plotLabel = `${sampleType}, n=${values.length}`;
|
|
77
|
+
const plot = Object.assign(_plot, {
|
|
78
|
+
key: sampleType
|
|
79
|
+
});
|
|
80
|
+
plot.boxplot.label = plotLabel;
|
|
81
|
+
plots.push(plot);
|
|
82
|
+
}
|
|
80
83
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
setHiddenPlots(overlayTerm, plots);
|
|
88
|
-
if (q.orderByMedian == true) {
|
|
89
|
-
plots.sort((a, b) => a.boxplot.p50 - b.boxplot.p50);
|
|
84
|
+
if (q.tw.term?.values) setHiddenPlots(q.tw, plots);
|
|
85
|
+
if (overlayTerm && overlayTerm.term?.values) setHiddenPlots(overlayTerm, plots);
|
|
86
|
+
if (q.orderByMedian == true) {
|
|
87
|
+
plots.sort((a, b) => a.boxplot.p50 - b.boxplot.p50);
|
|
88
|
+
}
|
|
89
|
+
charts[chart] = { chartId: chart, plots };
|
|
90
90
|
}
|
|
91
91
|
const returnData = {
|
|
92
92
|
absMin,
|
|
93
93
|
absMax,
|
|
94
|
-
|
|
94
|
+
charts,
|
|
95
95
|
uncomputableValues: setUncomputableValues(uncomputableValues)
|
|
96
96
|
};
|
|
97
97
|
res.send(returnData);
|
|
98
98
|
} catch (e) {
|
|
99
99
|
res.send({ error: e?.message || e });
|
|
100
|
-
if (e instanceof Error && e.stack)
|
|
101
|
-
console.error(e);
|
|
100
|
+
if (e instanceof Error && e.stack) console.error(e);
|
|
102
101
|
}
|
|
103
102
|
};
|
|
104
103
|
}
|
|
105
104
|
function setHiddenPlots(term, plots) {
|
|
106
105
|
for (const v of Object.values(term.term?.values)) {
|
|
107
106
|
const plot = plots.find((p) => p.key === v.label);
|
|
108
|
-
if (plot)
|
|
109
|
-
plot.isHidden = v?.uncomputable;
|
|
107
|
+
if (plot) plot.isHidden = v?.uncomputable;
|
|
110
108
|
}
|
|
111
109
|
if (term.q?.hiddenValues) {
|
|
112
110
|
for (const key of Object.keys(term.q.hiddenValues)) {
|
|
113
111
|
const plot = plots.find((p) => p.key === key);
|
|
114
|
-
if (plot)
|
|
115
|
-
plot.isHidden = true;
|
|
112
|
+
if (plot) plot.isHidden = true;
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
return plots;
|
|
119
116
|
}
|
|
120
117
|
function setDescrStats(boxplot, sortedValues) {
|
|
121
|
-
if (sortedValues.length < minSampleSize)
|
|
122
|
-
return [{ id: "total", label: "Total", value: sortedValues.length }];
|
|
118
|
+
if (sortedValues.length < minSampleSize) return [{ id: "total", label: "Total", value: sortedValues.length }];
|
|
123
119
|
const mean = getMean(sortedValues);
|
|
124
120
|
const variance = getVariance(sortedValues);
|
|
125
121
|
const sd = Math.sqrt(variance);
|
|
@@ -139,46 +135,50 @@ function setDescrStats(boxplot, sortedValues) {
|
|
|
139
135
|
function setUncomputableValues(values) {
|
|
140
136
|
if (Object.entries(values)?.length) {
|
|
141
137
|
return Object.entries(values).map(([label, v]) => ({ label, value: v }));
|
|
142
|
-
} else
|
|
143
|
-
return null;
|
|
138
|
+
} else return null;
|
|
144
139
|
}
|
|
145
|
-
function parseValues(q, data, sampleType, isLog, overlayTerm) {
|
|
146
|
-
const
|
|
140
|
+
function parseValues(q, data, sampleType, isLog, overlayTerm, divideTerm) {
|
|
141
|
+
const chart2plot2values = /* @__PURE__ */ new Map();
|
|
147
142
|
const uncomputableValues = {};
|
|
148
143
|
let absMin = null, absMax = null;
|
|
149
144
|
for (const val of Object.values(data.samples)) {
|
|
150
145
|
const value = val[q.tw.$id];
|
|
151
|
-
if (!Number.isFinite(value?.value))
|
|
152
|
-
continue;
|
|
146
|
+
if (!Number.isFinite(value?.value)) continue;
|
|
153
147
|
if (q.tw.term.values?.[value.value]?.uncomputable) {
|
|
154
148
|
const label = q.tw.term.values[value.value].label;
|
|
155
149
|
uncomputableValues[label] = (uncomputableValues[label] || 0) + 1;
|
|
156
150
|
continue;
|
|
157
151
|
}
|
|
158
|
-
if (isLog && value.value <= 0)
|
|
159
|
-
|
|
152
|
+
if (isLog && value.value <= 0) continue;
|
|
153
|
+
let chart = "";
|
|
154
|
+
let plot = sampleType;
|
|
155
|
+
if (divideTerm) {
|
|
156
|
+
if (!val[divideTerm?.$id]) continue;
|
|
157
|
+
const value0 = val[divideTerm.$id];
|
|
158
|
+
if (divideTerm.term?.values?.[value0.key]?.uncomputable) {
|
|
159
|
+
const label = divideTerm.term.values[value0?.key]?.label;
|
|
160
|
+
uncomputableValues[label] = (uncomputableValues[label] || 0) + 1;
|
|
161
|
+
}
|
|
162
|
+
chart = value0.key;
|
|
163
|
+
}
|
|
160
164
|
if (overlayTerm) {
|
|
161
|
-
if (!val[overlayTerm?.$id])
|
|
162
|
-
continue;
|
|
165
|
+
if (!val[overlayTerm?.$id]) continue;
|
|
163
166
|
const value2 = val[overlayTerm.$id];
|
|
164
167
|
if (overlayTerm.term?.values?.[value2.key]?.uncomputable) {
|
|
165
168
|
const label = overlayTerm.term.values[value2?.key]?.label;
|
|
166
169
|
uncomputableValues[label] = (uncomputableValues[label] || 0) + 1;
|
|
167
170
|
}
|
|
168
|
-
|
|
169
|
-
key2values.set(value2.key, []);
|
|
170
|
-
key2values.get(value2.key).push(value.value);
|
|
171
|
-
} else {
|
|
172
|
-
if (!key2values.has(sampleType))
|
|
173
|
-
key2values.set(sampleType, []);
|
|
174
|
-
key2values.get(sampleType).push(value.value);
|
|
171
|
+
plot = value2.key;
|
|
175
172
|
}
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
|
|
173
|
+
if (!chart2plot2values.has(chart)) chart2plot2values.set(chart, /* @__PURE__ */ new Map());
|
|
174
|
+
const plot2values = chart2plot2values.get(chart);
|
|
175
|
+
if (!plot2values.has(plot)) plot2values.set(plot, []);
|
|
176
|
+
const values = plot2values.get(plot);
|
|
177
|
+
values.push(value.value);
|
|
178
|
+
if (absMin === null || value.value < absMin) absMin = value.value;
|
|
179
|
+
if (absMax === null || value.value > absMax) absMax = value.value;
|
|
180
180
|
}
|
|
181
|
-
return { absMax, absMin,
|
|
181
|
+
return { absMax, absMin, chart2plot2values, uncomputableValues };
|
|
182
182
|
}
|
|
183
183
|
function numericBins(overlayTerm, data) {
|
|
184
184
|
const overlayBins = data.refs.byTermId[overlayTerm?.$id]?.bins ?? [];
|
|
@@ -19,25 +19,20 @@ function init({ genomes }) {
|
|
|
19
19
|
const q = req.query;
|
|
20
20
|
try {
|
|
21
21
|
const g = genomes[req.query.genome];
|
|
22
|
-
if (!g)
|
|
23
|
-
throw "invalid genome name";
|
|
22
|
+
if (!g) throw "invalid genome name";
|
|
24
23
|
const ds = g.datasets[req.query.dslabel];
|
|
25
|
-
if (!ds)
|
|
26
|
-
throw "invalid dataset name";
|
|
24
|
+
if (!ds) throw "invalid dataset name";
|
|
27
25
|
const tdb = ds.cohort.termdb;
|
|
28
|
-
if (!tdb)
|
|
29
|
-
throw "invalid termdb object";
|
|
26
|
+
if (!tdb) throw "invalid termdb object";
|
|
30
27
|
await trigger_getcategories(q, res, tdb, ds);
|
|
31
28
|
} catch (e) {
|
|
32
29
|
res.send({ error: e?.message || e });
|
|
33
|
-
if (e instanceof Error && e.stack)
|
|
34
|
-
console.log(e);
|
|
30
|
+
if (e instanceof Error && e.stack) console.log(e);
|
|
35
31
|
}
|
|
36
32
|
};
|
|
37
33
|
}
|
|
38
34
|
async function trigger_getcategories(q, res, tdb, ds) {
|
|
39
|
-
if (!q.tw.$id)
|
|
40
|
-
q.tw.$id = "_";
|
|
35
|
+
if (!q.tw.$id) q.tw.$id = "_";
|
|
41
36
|
const $id = q.tw.$id;
|
|
42
37
|
const arg = {
|
|
43
38
|
filter: q.filter,
|
|
@@ -50,8 +45,7 @@ async function trigger_getcategories(q, res, tdb, ds) {
|
|
|
50
45
|
__protected__: q.__protected__
|
|
51
46
|
};
|
|
52
47
|
const data = await getData(arg, ds);
|
|
53
|
-
if (data.error)
|
|
54
|
-
throw data.error;
|
|
48
|
+
if (data.error) throw data.error;
|
|
55
49
|
const [lst, orderedLabels] = getCategories(data, q, ds, $id);
|
|
56
50
|
res.send({
|
|
57
51
|
lst,
|
|
@@ -74,8 +68,7 @@ function getCategories(data, q, ds, $id) {
|
|
|
74
68
|
const sampleCountedFor = /* @__PURE__ */ new Set();
|
|
75
69
|
for (const sampleData of Object.values(samples)) {
|
|
76
70
|
const key = $id;
|
|
77
|
-
if (!Object.keys(sampleData).includes(key))
|
|
78
|
-
continue;
|
|
71
|
+
if (!Object.keys(sampleData).includes(key)) continue;
|
|
79
72
|
const values = sampleData[key].values;
|
|
80
73
|
sampleCountedFor.clear();
|
|
81
74
|
for (const value of values) {
|
|
@@ -114,10 +107,8 @@ function getCategories(data, q, ds, $id) {
|
|
|
114
107
|
const key2count = /* @__PURE__ */ new Map();
|
|
115
108
|
for (const sid in data.samples) {
|
|
116
109
|
const v = data.samples[sid][$id];
|
|
117
|
-
if (!v)
|
|
118
|
-
|
|
119
|
-
if (!("key" in v))
|
|
120
|
-
continue;
|
|
110
|
+
if (!v) continue;
|
|
111
|
+
if (!("key" in v)) continue;
|
|
121
112
|
key2count.set(v.key, 1 + (key2count.get(v.key) || 0));
|
|
122
113
|
}
|
|
123
114
|
for (const [key, count] of key2count) {
|
package/routes/termdb.cluster.js
CHANGED
|
@@ -31,20 +31,16 @@ function init({ genomes }) {
|
|
|
31
31
|
let result;
|
|
32
32
|
try {
|
|
33
33
|
const g = genomes[q.genome];
|
|
34
|
-
if (!g)
|
|
35
|
-
throw "invalid genome name";
|
|
34
|
+
if (!g) throw "invalid genome name";
|
|
36
35
|
const ds = g.datasets[q.dslabel];
|
|
37
|
-
if (!ds)
|
|
38
|
-
throw "invalid dataset name";
|
|
36
|
+
if (!ds) throw "invalid dataset name";
|
|
39
37
|
if (ds.label === "GDC" && !ds.__gdc?.doneCaching)
|
|
40
38
|
throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
|
|
41
39
|
if ([TermTypes.GENE_EXPRESSION, TermTypes.METABOLITE_INTENSITY, NUMERIC_DICTIONARY_TERM].includes(q.dataType)) {
|
|
42
40
|
if (!ds.queries?.[q.dataType] && q.dataType !== NUMERIC_DICTIONARY_TERM)
|
|
43
41
|
throw `no ${q.dataType} data on this dataset`;
|
|
44
|
-
if (!q.terms)
|
|
45
|
-
|
|
46
|
-
if (!Array.isArray(q.terms))
|
|
47
|
-
throw `gene list is not an array`;
|
|
42
|
+
if (!q.terms) throw `missing gene list`;
|
|
43
|
+
if (!Array.isArray(q.terms)) throw `gene list is not an array`;
|
|
48
44
|
if (q.terms.length < 3)
|
|
49
45
|
throw `A minimum of three genes is required for clustering. Please refresh this page to clear this error.`;
|
|
50
46
|
result = await getResult(q, ds);
|
|
@@ -52,8 +48,7 @@ function init({ genomes }) {
|
|
|
52
48
|
throw "unknown q.dataType " + q.dataType;
|
|
53
49
|
}
|
|
54
50
|
} catch (e) {
|
|
55
|
-
if (e.stack)
|
|
56
|
-
console.log(e.stack);
|
|
51
|
+
if (e.stack) console.log(e.stack);
|
|
57
52
|
result = {
|
|
58
53
|
status: e.status || 400,
|
|
59
54
|
error: e.message || e
|
|
@@ -94,8 +89,7 @@ async function getResult(q, ds) {
|
|
|
94
89
|
if (skippedSexChrGenes?.length) {
|
|
95
90
|
removedHierClusterTerms.push({ text: "Skipped sex chromosome genes", lst: skippedSexChrGenes });
|
|
96
91
|
}
|
|
97
|
-
if (term2sample2value.size == 0)
|
|
98
|
-
throw "no data";
|
|
92
|
+
if (term2sample2value.size == 0) throw "no data";
|
|
99
93
|
if (term2sample2value.size == 1) {
|
|
100
94
|
const g = Array.from(term2sample2value.keys())[0];
|
|
101
95
|
return { term: { gene: g, type: TermTypes.GENE_EXPRESSION }, data: term2sample2value.get(g) };
|
|
@@ -104,8 +98,7 @@ async function getResult(q, ds) {
|
|
|
104
98
|
const clustering = await doClustering(term2sample2value, q, Object.keys(bySampleId).length);
|
|
105
99
|
mayLog("clustering done:", formatElapsedTime(Date.now() - t));
|
|
106
100
|
const result = { clustering, byTermId, bySampleId };
|
|
107
|
-
if (removedHierClusterTerms.length)
|
|
108
|
-
result.removedHierClusterTerms = removedHierClusterTerms;
|
|
101
|
+
if (removedHierClusterTerms.length) result.removedHierClusterTerms = removedHierClusterTerms;
|
|
109
102
|
return result;
|
|
110
103
|
}
|
|
111
104
|
async function getNumericDictTermAnnotation(q, ds) {
|
|
@@ -149,10 +142,8 @@ async function doClustering(data, q, numCases = 1e3) {
|
|
|
149
142
|
throw `termdb.cluster: There are no overlapping tested samples shared across the selected ${termType2label(
|
|
150
143
|
q.dataType
|
|
151
144
|
)}`;
|
|
152
|
-
if (!clusterMethodLst.find((i) => i.value == q.clusterMethod))
|
|
153
|
-
|
|
154
|
-
if (!distanceMethodLst.find((i) => i.value == q.distanceMethod))
|
|
155
|
-
throw "Invalid distance method";
|
|
145
|
+
if (!clusterMethodLst.find((i) => i.value == q.clusterMethod)) throw "Invalid cluster method";
|
|
146
|
+
if (!distanceMethodLst.find((i) => i.value == q.distanceMethod)) throw "Invalid distance method";
|
|
156
147
|
const inputData = {
|
|
157
148
|
matrix: [],
|
|
158
149
|
row_names: [],
|
|
@@ -172,8 +163,7 @@ async function doClustering(data, q, numCases = 1e3) {
|
|
|
172
163
|
}
|
|
173
164
|
inputData.matrix.push(q.zScoreTransformation ? getZscore(row) : row);
|
|
174
165
|
}
|
|
175
|
-
if (inputData.matrix.length == 0)
|
|
176
|
-
throw "Clustering matrix is empty";
|
|
166
|
+
if (inputData.matrix.length == 0) throw "Clustering matrix is empty";
|
|
177
167
|
const Routput = JSON.parse(await run_R("hclust.R", JSON.stringify(inputData)));
|
|
178
168
|
const row_names_index = Routput.RowOrder.map((row) => inputData.row_names.indexOf(row.name));
|
|
179
169
|
const col_names_index = Routput.ColOrder.map((col) => inputData.col_names.indexOf(col.name));
|
|
@@ -211,8 +201,7 @@ function getZscore(l) {
|
|
|
211
201
|
}
|
|
212
202
|
async function validate_query_geneExpression(ds, genome) {
|
|
213
203
|
const q = ds.queries.geneExpression;
|
|
214
|
-
if (!q)
|
|
215
|
-
return;
|
|
204
|
+
if (!q) return;
|
|
216
205
|
q.geneExpression2bins = {};
|
|
217
206
|
if (q.src == "gdcapi") {
|
|
218
207
|
gdc_validate_query_geneExpression(ds, genome);
|
|
@@ -268,25 +257,20 @@ async function validateNative(q, ds) {
|
|
|
268
257
|
tmp = await run_rust("validateHDF5", JSON.stringify({ hdf5_file: q.file }));
|
|
269
258
|
}
|
|
270
259
|
const vr = JSON.parse(tmp);
|
|
271
|
-
if (vr.status !== "success")
|
|
272
|
-
throw vr.message;
|
|
260
|
+
if (vr.status !== "success") throw vr.message;
|
|
273
261
|
if (q.newformat) {
|
|
274
|
-
if (!vr.samples?.length)
|
|
275
|
-
throw "HDF5 file has no samples, please check file.";
|
|
262
|
+
if (!vr.samples?.length) throw "HDF5 file has no samples, please check file.";
|
|
276
263
|
for (const sn of vr.samples) {
|
|
277
264
|
const si = ds.cohort.termdb.q.sampleName2id(sn);
|
|
278
|
-
if (si == void 0)
|
|
279
|
-
throw `unknown sample ${sn} from HDF5 ${q.file}`;
|
|
265
|
+
if (si == void 0) throw `unknown sample ${sn} from HDF5 ${q.file}`;
|
|
280
266
|
q.samples.push(si);
|
|
281
267
|
}
|
|
282
268
|
console.log(`${ds.label}: geneExpression HDF5 file validated. Format: ${vr.format}, Samples:`, vr.samples.length);
|
|
283
269
|
} else {
|
|
284
|
-
if (!vr.sampleNames?.length)
|
|
285
|
-
throw "HDF5 file has no samples, please check file.";
|
|
270
|
+
if (!vr.sampleNames?.length) throw "HDF5 file has no samples, please check file.";
|
|
286
271
|
for (const sn of vr.sampleNames) {
|
|
287
272
|
const si = ds.cohort.termdb.q.sampleName2id(sn);
|
|
288
|
-
if (si == void 0)
|
|
289
|
-
throw `unknown sample ${sn} from HDF5 ${q.file}`;
|
|
273
|
+
if (si == void 0) throw `unknown sample ${sn} from HDF5 ${q.file}`;
|
|
290
274
|
q.samples.push(si);
|
|
291
275
|
}
|
|
292
276
|
console.log(
|
|
@@ -335,11 +319,9 @@ async function validateNative(q, ds) {
|
|
|
335
319
|
mayLog("Time taken to run gene query:", formatElapsedTime(Date.now() - time1));
|
|
336
320
|
if (q.newformat) {
|
|
337
321
|
const genesData = geneData.query_output || {};
|
|
338
|
-
if (!genesData)
|
|
339
|
-
throw "No expression data returned from HDF5 query";
|
|
322
|
+
if (!genesData) throw "No expression data returned from HDF5 query";
|
|
340
323
|
for (const tw of param.terms) {
|
|
341
|
-
if (!tw.term.gene)
|
|
342
|
-
continue;
|
|
324
|
+
if (!tw.term.gene) continue;
|
|
343
325
|
const geneResult = genesData[tw.term.gene];
|
|
344
326
|
if (!geneResult) {
|
|
345
327
|
console.warn(`No data found for gene ${tw.term.gene} in the response`);
|
|
@@ -349,10 +331,8 @@ async function validateNative(q, ds) {
|
|
|
349
331
|
const s2v = {};
|
|
350
332
|
for (const sampleName in samplesData) {
|
|
351
333
|
const sampleId = ds.cohort.termdb.q.sampleName2id(sampleName);
|
|
352
|
-
if (!sampleId)
|
|
353
|
-
|
|
354
|
-
if (limitSamples && !limitSamples.has(sampleId))
|
|
355
|
-
continue;
|
|
334
|
+
if (!sampleId) continue;
|
|
335
|
+
if (limitSamples && !limitSamples.has(sampleId)) continue;
|
|
356
336
|
s2v[sampleId] = samplesData[sampleName];
|
|
357
337
|
}
|
|
358
338
|
if (Object.keys(s2v).length) {
|
|
@@ -365,8 +345,7 @@ async function validateNative(q, ds) {
|
|
|
365
345
|
} else {
|
|
366
346
|
const genesData = geneData.genes || { [geneNames[0]]: geneData };
|
|
367
347
|
for (const tw of param.terms) {
|
|
368
|
-
if (!tw.term.gene)
|
|
369
|
-
continue;
|
|
348
|
+
if (!tw.term.gene) continue;
|
|
370
349
|
const geneResult = genesData[tw.term.gene];
|
|
371
350
|
if (!geneResult) {
|
|
372
351
|
console.warn(`No data found for gene ${tw.term.gene} in the response`);
|
|
@@ -376,10 +355,8 @@ async function validateNative(q, ds) {
|
|
|
376
355
|
const s2v = {};
|
|
377
356
|
for (const [sampleName, value] of Object.entries(samplesData)) {
|
|
378
357
|
const sampleId = ds.cohort.termdb.q.sampleName2id(sampleName);
|
|
379
|
-
if (!sampleId)
|
|
380
|
-
|
|
381
|
-
if (limitSamples && !limitSamples.has(sampleId))
|
|
382
|
-
continue;
|
|
358
|
+
if (!sampleId) continue;
|
|
359
|
+
if (limitSamples && !limitSamples.has(sampleId)) continue;
|
|
383
360
|
s2v[sampleId] = value;
|
|
384
361
|
}
|
|
385
362
|
if (Object.keys(s2v).length) {
|
|
@@ -16,17 +16,14 @@ function init({ genomes }) {
|
|
|
16
16
|
mayCopyFromCookie(q, req.cookies);
|
|
17
17
|
try {
|
|
18
18
|
const genome = genomes[q.genome];
|
|
19
|
-
if (!genome)
|
|
20
|
-
throw "invalid genome";
|
|
19
|
+
if (!genome) throw "invalid genome";
|
|
21
20
|
const [ds] = get_ds_tdb(genome, q);
|
|
22
21
|
const count = ds.cohort.termdb.q?.getCohortSampleCount?.(q.cohort) || 1;
|
|
23
22
|
res.send({ count });
|
|
24
23
|
} catch (e) {
|
|
25
24
|
res.send({ error: e.message || e });
|
|
26
|
-
if (e.stack)
|
|
27
|
-
|
|
28
|
-
else
|
|
29
|
-
console.log(e);
|
|
25
|
+
if (e.stack) console.log(e.stack);
|
|
26
|
+
else console.log(e);
|
|
30
27
|
}
|
|
31
28
|
};
|
|
32
29
|
}
|
package/routes/termdb.cohorts.js
CHANGED
|
@@ -16,23 +16,19 @@ function init({ genomes }) {
|
|
|
16
16
|
mayCopyFromCookie(q, req.cookies);
|
|
17
17
|
try {
|
|
18
18
|
const genome = genomes[q.genome];
|
|
19
|
-
if (!genome)
|
|
20
|
-
throw "invalid genome";
|
|
19
|
+
if (!genome) throw "invalid genome";
|
|
21
20
|
const [ds] = get_ds_tdb(genome, q);
|
|
22
21
|
const result = getCohortsData(ds);
|
|
23
22
|
res.send(result);
|
|
24
23
|
} catch (e) {
|
|
25
24
|
res.send({ error: e.message || e });
|
|
26
|
-
if (e.stack)
|
|
27
|
-
|
|
28
|
-
else
|
|
29
|
-
console.log(e);
|
|
25
|
+
if (e.stack) console.log(e.stack);
|
|
26
|
+
else console.log(e);
|
|
30
27
|
}
|
|
31
28
|
};
|
|
32
29
|
}
|
|
33
30
|
function getCohortsData(ds) {
|
|
34
|
-
if (!ds.cohort.db)
|
|
35
|
-
return { cohorts: [], features: [], cfeatures: [] };
|
|
31
|
+
if (!ds.cohort.db) return { cohorts: [], features: [], cfeatures: [] };
|
|
36
32
|
const features = ds.cohort.db.connection.prepare("select * from features").all();
|
|
37
33
|
const cohorts = ds.cohort.db.connection.prepare(
|
|
38
34
|
`select * from cohorts where cohort in (select distinct(cohort) from cohort_features)
|