@sjcrh/proteinpaint-server 2.65.0 → 2.67.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 +3 -3
- package/routes/termdb.categories.js +9 -7
- package/routes/termdb.cluster.js +6 -2
- package/routes/termdb.config.js +7 -1
- package/routes/termdb.singlecellDEgenes.js +51 -0
- package/routes/termdb.singlecellSamples.js +35 -51
- package/routes/termdb.termsbyids.js +3 -1
- package/routes/termdb.topVariablyExpressedGenes.js +12 -12
- package/src/app.js +1437 -974
- package/utils/getGeneFromMatrix.R +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.67.0",
|
|
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",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"postpack": "./dedupjs.sh",
|
|
28
28
|
"dedup": "./dedupjs.sh",
|
|
29
29
|
"//todo": "refactor or deprecate the scripts below",
|
|
30
|
-
"pretest": "tsc
|
|
30
|
+
"pretest": "tsc && ./test/pretest.js",
|
|
31
31
|
"prepare": "ts-patch install",
|
|
32
32
|
"pretest:type": "npm run checkers",
|
|
33
|
-
"pretest:integration": "tsc
|
|
33
|
+
"pretest:integration": "tsc",
|
|
34
34
|
"test:integration": "echo 'TODO: server integration tests'",
|
|
35
35
|
"test:tsc": "tsc --esModuleInterop --noEmit --allowImportingTsExtensions ./shared/types/test/*.type.spec.ts"
|
|
36
36
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getOrderedLabels } from "#src/termdb.barchart.js";
|
|
2
2
|
import { getData } from "#src/termdb.matrix.js";
|
|
3
|
+
import { TermTypes } from "#shared/terms.js";
|
|
3
4
|
const api = {
|
|
4
5
|
endpoint: "termdb/categories",
|
|
5
6
|
methods: {
|
|
@@ -78,11 +79,10 @@ function init({ genomes }) {
|
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
81
|
async function trigger_getcategories(q, res, tdb, ds, genome) {
|
|
81
|
-
const $id =
|
|
82
|
-
const tw = { $id, term: q.term, q: q.term1_q || getDefaultQ(q.term, q) };
|
|
82
|
+
const $id = q.tw.$id;
|
|
83
83
|
const arg = {
|
|
84
84
|
filter: q.filter,
|
|
85
|
-
terms: [tw],
|
|
85
|
+
terms: [q.tw],
|
|
86
86
|
currentGeneNames: q.currentGeneNames,
|
|
87
87
|
// optional, from mds3 mayAddGetCategoryArgs()
|
|
88
88
|
rglst: q.rglst
|
|
@@ -92,7 +92,7 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
|
|
|
92
92
|
if (data.error)
|
|
93
93
|
throw data.error;
|
|
94
94
|
const lst = [];
|
|
95
|
-
if (q.term.type == "geneVariant") {
|
|
95
|
+
if (q.tw.term.type == "geneVariant" && !q.tw.q.groupsetting.inuse) {
|
|
96
96
|
const samples = data.samples;
|
|
97
97
|
const dtClassMap = /* @__PURE__ */ new Map();
|
|
98
98
|
if (ds.assayAvailability?.byDt) {
|
|
@@ -153,15 +153,15 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
|
|
|
153
153
|
lst.push({
|
|
154
154
|
samplecount: count,
|
|
155
155
|
key,
|
|
156
|
-
label: data.refs?.byTermId?.[$id]?.events?.find((e) => e.event === key).label || q.term?.values?.[key]?.label || key
|
|
156
|
+
label: data.refs?.byTermId?.[$id]?.events?.find((e) => e.event === key).label || q.tw.term?.values?.[key]?.label || key
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
const orderedLabels = getOrderedLabels(
|
|
161
|
-
q.term,
|
|
161
|
+
q.tw.term,
|
|
162
162
|
data.refs?.byTermId?.[$id]?.bins || [],
|
|
163
163
|
data.refs?.byTermId?.[$id]?.events,
|
|
164
|
-
q.
|
|
164
|
+
q.tw.q
|
|
165
165
|
);
|
|
166
166
|
if (orderedLabels.length) {
|
|
167
167
|
lst.sort((a, b) => orderedLabels.indexOf(a.label) - orderedLabels.indexOf(b.label));
|
|
@@ -195,6 +195,8 @@ function getDefaultQ(term, q) {
|
|
|
195
195
|
}
|
|
196
196
|
if (term.type == "geneVariant")
|
|
197
197
|
return {};
|
|
198
|
+
if (term.type == TermTypes.SINGLECELL_CELLTYPE)
|
|
199
|
+
return {};
|
|
198
200
|
throw "unknown term type";
|
|
199
201
|
}
|
|
200
202
|
export {
|
package/routes/termdb.cluster.js
CHANGED
|
@@ -53,8 +53,12 @@ function init({ genomes }) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
async function getResult(q, ds) {
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
let _q = q;
|
|
57
|
+
if (q.dataType == TermTypes.GENE_EXPRESSION) {
|
|
58
|
+
_q = JSON.parse(JSON.stringify(q));
|
|
59
|
+
_q.forClusteringAnalysis = true;
|
|
60
|
+
}
|
|
61
|
+
const { term2sample2value, byTermId, bySampleId } = await ds.queries[q.dataType].get(_q);
|
|
58
62
|
if (term2sample2value.size == 0)
|
|
59
63
|
throw "no data";
|
|
60
64
|
if (term2sample2value.size == 1) {
|
package/routes/termdb.config.js
CHANGED
|
@@ -199,12 +199,18 @@ function addNonDictionaryQueries(c, ds, genome) {
|
|
|
199
199
|
},
|
|
200
200
|
data: {
|
|
201
201
|
sameLegend: q.singleCell.data.sameLegend,
|
|
202
|
-
refName: q.singleCell.data.refName
|
|
202
|
+
refName: q.singleCell.data.refName,
|
|
203
|
+
plots: q.singleCell.data.plots.map((p) => {
|
|
204
|
+
return { name: p.name, colorColumn: p.colorColumn.name };
|
|
205
|
+
})
|
|
203
206
|
}
|
|
204
207
|
};
|
|
205
208
|
if (q.singleCell.geneExpression) {
|
|
206
209
|
q2.singleCell.geneExpression = {};
|
|
207
210
|
}
|
|
211
|
+
if (q.singleCell.DEgenes) {
|
|
212
|
+
q2.singleCell.DEgenes = { columnName: q.singleCell.DEgenes.columnName };
|
|
213
|
+
}
|
|
208
214
|
}
|
|
209
215
|
if (q.images) {
|
|
210
216
|
q2.images = {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { gdc_validate_query_singleCell_DEgenes } from "#src/mds3.gdc.js";
|
|
2
|
+
const api = {
|
|
3
|
+
endpoint: "termdb/singlecellDEgenes",
|
|
4
|
+
methods: {
|
|
5
|
+
all: {
|
|
6
|
+
init,
|
|
7
|
+
request: {
|
|
8
|
+
typeId: "TermdbSinglecellDEgenesRequest"
|
|
9
|
+
},
|
|
10
|
+
response: {
|
|
11
|
+
typeId: "TermdbSinglecellDEgenesResponse"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function init({ genomes }) {
|
|
17
|
+
return async (req, res) => {
|
|
18
|
+
const q = req.query;
|
|
19
|
+
let result;
|
|
20
|
+
try {
|
|
21
|
+
const g = genomes[q.genome];
|
|
22
|
+
if (!g)
|
|
23
|
+
throw "invalid genome name";
|
|
24
|
+
const ds = g.datasets[q.dslabel];
|
|
25
|
+
if (!ds)
|
|
26
|
+
throw "invalid dataset name";
|
|
27
|
+
if (!ds.queries?.singleCell?.DEgenes)
|
|
28
|
+
throw "not supported on this dataset";
|
|
29
|
+
result = await ds.queries.singleCell.DEgenes.get(q);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
if (e.stack)
|
|
32
|
+
console.log(e.stack);
|
|
33
|
+
result = {
|
|
34
|
+
status: e.status || 400,
|
|
35
|
+
error: e.message || e
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
res.send(result);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function validate_query_singleCell_DEgenes(ds) {
|
|
42
|
+
if (ds.queries.singleCell.DEgenes.src == "gdcapi") {
|
|
43
|
+
gdc_validate_query_singleCell_DEgenes(ds);
|
|
44
|
+
} else {
|
|
45
|
+
throw "unknown singleCell.DEgenes.src";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
api,
|
|
50
|
+
validate_query_singleCell_DEgenes
|
|
51
|
+
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { read_file } from "#src/utils.js";
|
|
4
|
+
import run_R from "#src/run_R.js";
|
|
5
5
|
import serverconfig from "#src/serverconfig.js";
|
|
6
|
+
import { validate_query_singleCell_DEgenes } from "./termdb.singlecellDEgenes.ts";
|
|
6
7
|
import { gdc_validate_query_singleCell_samples, gdc_validate_query_singleCell_data } from "#src/mds3.gdc.js";
|
|
7
8
|
const api = {
|
|
8
9
|
endpoint: "termdb/singlecellSamples",
|
|
9
10
|
methods: {
|
|
10
|
-
|
|
11
|
+
all: {
|
|
11
12
|
init,
|
|
12
13
|
request: {
|
|
13
14
|
typeId: "TermdbSinglecellsamplesRequest"
|
|
@@ -15,10 +16,6 @@ const api = {
|
|
|
15
16
|
response: {
|
|
16
17
|
typeId: "TermdbSinglecellsamplesResponse"
|
|
17
18
|
}
|
|
18
|
-
},
|
|
19
|
-
post: {
|
|
20
|
-
alternativeFor: "get",
|
|
21
|
-
init
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
21
|
};
|
|
@@ -73,6 +70,9 @@ async function validate_query_singleCell(ds, genome) {
|
|
|
73
70
|
throw "unknown singleCell.geneExpression.src";
|
|
74
71
|
}
|
|
75
72
|
}
|
|
73
|
+
if (q.DEgenes) {
|
|
74
|
+
validate_query_singleCell_DEgenes(ds);
|
|
75
|
+
}
|
|
76
76
|
}
|
|
77
77
|
async function validateSamplesNative(S, ds) {
|
|
78
78
|
const samples = {};
|
|
@@ -104,19 +104,25 @@ function validateDataNative(D, ds) {
|
|
|
104
104
|
if (ds.queries.singleCell.geneExpression && q.gene) {
|
|
105
105
|
geneExpMap = await ds.queries.singleCell.geneExpression.get({ sample: q.sample, gene: q.gene });
|
|
106
106
|
}
|
|
107
|
+
const file2Lines = {};
|
|
107
108
|
for (const plot of D.plots) {
|
|
109
|
+
if (!q.plots.includes(plot.name))
|
|
110
|
+
continue;
|
|
108
111
|
const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, q.sample + plot.fileSuffix);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
if (!file2Lines[tsvfile]) {
|
|
113
|
+
try {
|
|
114
|
+
await fs.promises.stat(tsvfile);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
if (e.code == "ENOENT") {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (e.code == "EACCES")
|
|
120
|
+
throw "cannot read file, permission denied";
|
|
121
|
+
throw "failed to load sc data file";
|
|
114
122
|
}
|
|
115
|
-
|
|
116
|
-
throw "cannot read file, permission denied";
|
|
117
|
-
throw "failed to load sc data file";
|
|
123
|
+
file2Lines[tsvfile] = (await read_file(tsvfile)).trim().split("\n");
|
|
118
124
|
}
|
|
119
|
-
const lines =
|
|
125
|
+
const lines = file2Lines[tsvfile];
|
|
120
126
|
const cells = [];
|
|
121
127
|
for (let i = 1; i < lines.length; i++) {
|
|
122
128
|
const l = lines[i].split(" ");
|
|
@@ -148,47 +154,25 @@ function validateDataNative(D, ds) {
|
|
|
148
154
|
};
|
|
149
155
|
}
|
|
150
156
|
function validateGeneExpressionNative(G) {
|
|
157
|
+
G.sample2gene2expressionBins = {};
|
|
151
158
|
G.get = async (q) => {
|
|
152
|
-
const
|
|
159
|
+
const rdsfile = path.join(serverconfig.tpmasterdir, G.folder, q.sample + ".rds");
|
|
160
|
+
try {
|
|
161
|
+
await fs.promises.stat(rdsfile);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
let out;
|
|
153
166
|
try {
|
|
154
|
-
|
|
167
|
+
out = JSON.parse(
|
|
168
|
+
await run_R(path.join(serverconfig.binpath, "utils", "getGeneFromMatrix.R"), null, [rdsfile, q.gene])
|
|
169
|
+
);
|
|
155
170
|
} catch (e) {
|
|
156
|
-
|
|
171
|
+
return {};
|
|
157
172
|
}
|
|
158
|
-
|
|
159
|
-
if (header.length == 0)
|
|
160
|
-
throw "blank header line";
|
|
161
|
-
return await grepMatrix4geneExpression(tsvfile, q.gene, header);
|
|
173
|
+
return out;
|
|
162
174
|
};
|
|
163
175
|
}
|
|
164
|
-
function grepMatrix4geneExpression(tsvfile, gene, header) {
|
|
165
|
-
return new Promise((resolve, reject) => {
|
|
166
|
-
const cp = spawn("grep", ["-m", "1", gene + " ", tsvfile]);
|
|
167
|
-
const out = [], err = [];
|
|
168
|
-
cp.stdout.on("data", (d) => out.push(d));
|
|
169
|
-
cp.stderr.on("data", (d) => err.push(d));
|
|
170
|
-
cp.on("close", () => {
|
|
171
|
-
const e = err.join("");
|
|
172
|
-
if (e)
|
|
173
|
-
reject(e);
|
|
174
|
-
const cell2value = {};
|
|
175
|
-
const line = out.join("").trim();
|
|
176
|
-
if (!line) {
|
|
177
|
-
resolve(cell2value);
|
|
178
|
-
}
|
|
179
|
-
const l = line.split(" ");
|
|
180
|
-
if (l.length != header.length)
|
|
181
|
-
reject(`number of fields differ between data line and header: ${l.length} ${header.length}`);
|
|
182
|
-
for (let i = 1; i < l.length; i++) {
|
|
183
|
-
const v = Number(l[i]);
|
|
184
|
-
if (Number.isNaN(v))
|
|
185
|
-
continue;
|
|
186
|
-
cell2value[header[i]] = v;
|
|
187
|
-
}
|
|
188
|
-
resolve(cell2value);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
176
|
export {
|
|
193
177
|
api,
|
|
194
178
|
validate_query_singleCell
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { gdcGetCasesWithExpressionDataFromCohort } from "../src/mds3.gdc.js";
|
|
2
1
|
import path from "path";
|
|
3
2
|
import { run_rust } from "@sjcrh/proteinpaint-rust";
|
|
4
3
|
import got from "got";
|
|
5
4
|
import serverconfig from "#src/serverconfig.js";
|
|
6
5
|
import { get_samples } from "#src/termdb.sql.js";
|
|
6
|
+
import { makeFilter } from "#src/mds3.gdc.js";
|
|
7
7
|
const api = {
|
|
8
8
|
endpoint: "termdb/topVariablyExpressedGenes",
|
|
9
9
|
methods: {
|
|
@@ -106,21 +106,20 @@ async function computeGenes4nativeDs(q, ds, matrixFile, samples) {
|
|
|
106
106
|
function gdcValidateQuery(ds, genome) {
|
|
107
107
|
ds.queries.topVariablyExpressedGenes.getGenes = async (q) => {
|
|
108
108
|
if (serverconfig.features.gdcGenes) {
|
|
109
|
-
console.log(
|
|
109
|
+
console.log(
|
|
110
|
+
"!!GDC!! using serverconfig.features.gdcGenes[] but not live api query. only use this on DEV and never on PROD!"
|
|
111
|
+
);
|
|
110
112
|
return serverconfig.features.gdcGenes;
|
|
111
113
|
}
|
|
112
|
-
if (!ds.__gdc.doneCaching)
|
|
114
|
+
if (!ds.__gdc.doneCaching) {
|
|
113
115
|
throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
|
|
114
|
-
const caseLst = await gdcGetCasesWithExpressionDataFromCohort(q, ds);
|
|
115
|
-
if (caseLst.length == 0) {
|
|
116
|
-
return [];
|
|
117
116
|
}
|
|
118
117
|
const { host, headers } = ds.getHostHeaders(q);
|
|
119
118
|
const url = path.join(host.geneExp, "/gene_expression/gene_selection");
|
|
120
119
|
try {
|
|
121
120
|
const response = await got.post(url, {
|
|
122
121
|
headers,
|
|
123
|
-
body: JSON.stringify(getGeneSelectionArg(q
|
|
122
|
+
body: JSON.stringify(getGeneSelectionArg(q))
|
|
124
123
|
});
|
|
125
124
|
const re = JSON.parse(response.body);
|
|
126
125
|
const genes = [];
|
|
@@ -143,13 +142,14 @@ function gdcValidateQuery(ds, genome) {
|
|
|
143
142
|
throw e;
|
|
144
143
|
}
|
|
145
144
|
};
|
|
146
|
-
function getGeneSelectionArg(q
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
gene_ids: tempGetCGCgenes(genome),
|
|
145
|
+
function getGeneSelectionArg(q) {
|
|
146
|
+
const arg = {
|
|
147
|
+
// add any to avoid tsc err
|
|
148
|
+
case_filters: makeFilter(q),
|
|
151
149
|
selection_size: Number(q.maxGenes)
|
|
152
150
|
};
|
|
151
|
+
arg.gene_ids = tempGetCGCgenes(genome);
|
|
152
|
+
return arg;
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
function tempGetCGCgenes(genome) {
|