@sjcrh/proteinpaint-server 2.135.2 → 2.136.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/termdb.test.js +1 -0
- package/package.json +5 -6
- package/routes/filterTermValues.js +2 -1
- package/routes/termdb.categories.js +14 -6
- package/routes/termdb.singlecellSamples.js +69 -57
- package/src/app.js +1838 -1796
- package/src/serverconfig.js +17 -1
package/dataset/termdb.test.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.136.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",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"babel-loader": "^8.2.2",
|
|
48
48
|
"c8": "^10.1.3",
|
|
49
49
|
"esbuild": "^0.19.12",
|
|
50
|
-
"glob": "^10.4.5",
|
|
51
50
|
"monocart-coverage-reports": "^2.12.1",
|
|
52
51
|
"node-notifier": "^9.0.1",
|
|
53
52
|
"node-watch": "^0.7.1",
|
|
@@ -60,12 +59,12 @@
|
|
|
60
59
|
"typescript": "^5.6.3"
|
|
61
60
|
},
|
|
62
61
|
"dependencies": {
|
|
63
|
-
"@sjcrh/augen": "2.
|
|
62
|
+
"@sjcrh/augen": "2.136.0",
|
|
64
63
|
"@sjcrh/proteinpaint-python": "2.135.2-0",
|
|
65
64
|
"@sjcrh/proteinpaint-r": "2.130.0",
|
|
66
|
-
"@sjcrh/proteinpaint-rust": "2.
|
|
67
|
-
"@sjcrh/proteinpaint-shared": "2.
|
|
68
|
-
"@sjcrh/proteinpaint-types": "2.
|
|
65
|
+
"@sjcrh/proteinpaint-rust": "2.136.0",
|
|
66
|
+
"@sjcrh/proteinpaint-shared": "2.136.0",
|
|
67
|
+
"@sjcrh/proteinpaint-types": "2.136.0",
|
|
69
68
|
"@types/express": "^5.0.0",
|
|
70
69
|
"@types/express-session": "^1.18.1",
|
|
71
70
|
"better-sqlite3": "^9.4.1",
|
|
@@ -35,7 +35,8 @@ function getList(samplesPerFilter, filtersData, tw) {
|
|
|
35
35
|
for (const sample of twSamples) {
|
|
36
36
|
data.push(filtersData.samples[sample]);
|
|
37
37
|
}
|
|
38
|
-
const
|
|
38
|
+
const annotations = data.filter((s) => s != void 0).map((sample) => sample[tw.$id]?.value);
|
|
39
|
+
const sampleValues = Array.from(new Set(annotations));
|
|
39
40
|
for (const value of values) {
|
|
40
41
|
value.value = value.label;
|
|
41
42
|
const label = value.label;
|
|
@@ -51,12 +51,20 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
|
|
|
51
51
|
const data = await getData(arg, ds, genome);
|
|
52
52
|
if (data.error)
|
|
53
53
|
throw data.error;
|
|
54
|
+
const [lst, orderedLabels] = getCategories(data, q, ds, $id);
|
|
55
|
+
res.send({
|
|
56
|
+
lst,
|
|
57
|
+
orderedLabels
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function getCategories(data, q, ds, $id) {
|
|
54
61
|
const lst = [];
|
|
55
62
|
if (q.tw.term.type == "geneVariant" && q.tw.q.type != "predefined-groupset" && q.tw.q.type != "custom-groupset") {
|
|
56
63
|
const samples = data.samples;
|
|
57
64
|
const dtClassMap = /* @__PURE__ */ new Map();
|
|
58
65
|
if (ds.assayAvailability?.byDt) {
|
|
59
|
-
for (const [dtType,
|
|
66
|
+
for (const [dtType, _dtValue] of Object.entries(ds.assayAvailability.byDt)) {
|
|
67
|
+
const dtValue = _dtValue;
|
|
60
68
|
if (dtValue.byOrigin) {
|
|
61
69
|
dtClassMap.set(parseInt(dtType), { byOrigin: { germline: {}, somatic: {} } });
|
|
62
70
|
}
|
|
@@ -65,6 +73,8 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
|
|
|
65
73
|
const sampleCountedFor = /* @__PURE__ */ new Set();
|
|
66
74
|
for (const sampleData of Object.values(samples)) {
|
|
67
75
|
const key = $id;
|
|
76
|
+
if (!Object.keys(sampleData).includes(key))
|
|
77
|
+
continue;
|
|
68
78
|
const values = sampleData[key].values;
|
|
69
79
|
sampleCountedFor.clear();
|
|
70
80
|
for (const value of values) {
|
|
@@ -126,11 +136,9 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
|
|
|
126
136
|
if (orderedLabels.length) {
|
|
127
137
|
lst.sort((a, b) => orderedLabels.indexOf(a.label) - orderedLabels.indexOf(b.label));
|
|
128
138
|
}
|
|
129
|
-
|
|
130
|
-
lst,
|
|
131
|
-
orderedLabels
|
|
132
|
-
});
|
|
139
|
+
return [lst, orderedLabels];
|
|
133
140
|
}
|
|
134
141
|
export {
|
|
135
|
-
api
|
|
142
|
+
api,
|
|
143
|
+
getCategories
|
|
136
144
|
};
|
|
@@ -51,9 +51,14 @@ async function validate_query_singleCell(ds, genome) {
|
|
|
51
51
|
const q = ds.queries.singleCell;
|
|
52
52
|
if (!q)
|
|
53
53
|
return;
|
|
54
|
-
if (typeof q.samples
|
|
54
|
+
if (typeof q.samples != "object")
|
|
55
|
+
throw "singleCell.samples{} not object";
|
|
56
|
+
if (typeof q.samples.get == "function") {
|
|
57
|
+
} else {
|
|
55
58
|
validateSamplesNative(q.samples, q.data, ds);
|
|
56
59
|
}
|
|
60
|
+
if (typeof q.data != "object")
|
|
61
|
+
throw "singleCell.data{} not object";
|
|
57
62
|
if (q.data.src == "gdcapi") {
|
|
58
63
|
gdc_validate_query_singleCell_data(ds, genome);
|
|
59
64
|
} else if (q.data.src == "native") {
|
|
@@ -62,6 +67,8 @@ async function validate_query_singleCell(ds, genome) {
|
|
|
62
67
|
throw "unknown singleCell.data.src";
|
|
63
68
|
}
|
|
64
69
|
if (q.geneExpression) {
|
|
70
|
+
if (typeof q.geneExpression != "object")
|
|
71
|
+
throw "singleCell.geneExpression not object";
|
|
65
72
|
if (q.geneExpression.src == "native") {
|
|
66
73
|
validateGeneExpressionNative(q.geneExpression);
|
|
67
74
|
} else if (q.geneExpression.src == "gdcapi") {
|
|
@@ -71,9 +78,13 @@ async function validate_query_singleCell(ds, genome) {
|
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
80
|
if (q.DEgenes) {
|
|
81
|
+
if (typeof q.DEgenes != "object")
|
|
82
|
+
throw "singleCell.DEgenes not object";
|
|
74
83
|
validate_query_singleCell_DEgenes(ds);
|
|
75
84
|
}
|
|
76
85
|
if (q.images) {
|
|
86
|
+
if (typeof q.images != "object")
|
|
87
|
+
throw "singleCell.images not object";
|
|
77
88
|
validateImages(q.images);
|
|
78
89
|
}
|
|
79
90
|
}
|
|
@@ -127,68 +138,69 @@ function validateDataNative(D, ds) {
|
|
|
127
138
|
if (nameSet.has(plot.name))
|
|
128
139
|
throw "duplicate plot.name";
|
|
129
140
|
nameSet.add(plot.name);
|
|
141
|
+
if (!plot.folder)
|
|
142
|
+
throw "plot.folder missing";
|
|
130
143
|
}
|
|
144
|
+
const file2Lines = {};
|
|
131
145
|
D.get = async (q) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const expCells = [];
|
|
154
|
-
const noExpCells = [];
|
|
155
|
-
for (let i = 1; i < lines.length; i++) {
|
|
156
|
-
const l = lines[i].split(" ");
|
|
157
|
-
const cellId = lines.length > 3 ? l[0] : void 0, x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
|
|
158
|
-
const category = l[colorColumn?.index] || "";
|
|
159
|
-
if (!cellId)
|
|
160
|
-
throw "cell id missing";
|
|
161
|
-
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
162
|
-
throw "x/y not number";
|
|
163
|
-
const cell = { cellId, x, y, category };
|
|
164
|
-
if (geneExpMap) {
|
|
165
|
-
if (geneExpMap[cellId] !== void 0) {
|
|
166
|
-
cell.geneExp = geneExpMap[cellId];
|
|
167
|
-
expCells.push(cell);
|
|
168
|
-
} else {
|
|
169
|
-
noExpCells.push(cell);
|
|
170
|
-
}
|
|
171
|
-
} else
|
|
172
|
-
noExpCells.push(cell);
|
|
146
|
+
const plots = [];
|
|
147
|
+
let geneExpMap;
|
|
148
|
+
if (ds.queries.singleCell.geneExpression && q.gene) {
|
|
149
|
+
geneExpMap = await ds.queries.singleCell.geneExpression.get({ sample: q.sample, gene: q.gene });
|
|
150
|
+
}
|
|
151
|
+
for (const plot of D.plots) {
|
|
152
|
+
if (!q.plots.includes(plot.name))
|
|
153
|
+
continue;
|
|
154
|
+
const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, (q.sample.eID || q.sample.sID) + plot.fileSuffix);
|
|
155
|
+
if (!file2Lines[tsvfile]) {
|
|
156
|
+
await file_is_readable(tsvfile);
|
|
157
|
+
const text = await read_file(tsvfile);
|
|
158
|
+
const lines = text.trim().split("\n");
|
|
159
|
+
let first = true;
|
|
160
|
+
const lines2 = [];
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
if (first) {
|
|
163
|
+
first = false;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
lines2.push(line.split(" "));
|
|
173
167
|
}
|
|
174
|
-
|
|
175
|
-
name: plot.name,
|
|
176
|
-
expCells,
|
|
177
|
-
noExpCells,
|
|
178
|
-
colorColumns: plot.colorColumns.map((c) => c.name),
|
|
179
|
-
colorBy: colorColumn?.name,
|
|
180
|
-
colorMap: colorColumn?.colorMap
|
|
181
|
-
});
|
|
168
|
+
file2Lines[tsvfile] = lines2;
|
|
182
169
|
}
|
|
183
|
-
|
|
184
|
-
|
|
170
|
+
const colorColumn = plot.colorColumns.find((c) => c.name == q.colorBy?.[plot.name]) || plot.colorColumns[0];
|
|
171
|
+
const expCells = [];
|
|
172
|
+
const noExpCells = [];
|
|
173
|
+
for (const l of file2Lines[tsvfile]) {
|
|
174
|
+
const cellId = l[0], x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
|
|
175
|
+
const category = l[colorColumn?.index] || "";
|
|
176
|
+
if (!cellId)
|
|
177
|
+
throw "cell id missing";
|
|
178
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
179
|
+
throw "x/y not number";
|
|
180
|
+
const cell = { cellId, x, y, category };
|
|
181
|
+
if (geneExpMap) {
|
|
182
|
+
if (geneExpMap[cellId] !== void 0) {
|
|
183
|
+
cell.geneExp = geneExpMap[cellId];
|
|
184
|
+
expCells.push(cell);
|
|
185
|
+
} else {
|
|
186
|
+
noExpCells.push(cell);
|
|
187
|
+
}
|
|
188
|
+
} else
|
|
189
|
+
noExpCells.push(cell);
|
|
185
190
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
plots.push({
|
|
192
|
+
name: plot.name,
|
|
193
|
+
expCells,
|
|
194
|
+
noExpCells,
|
|
195
|
+
colorColumns: plot.colorColumns.map((c) => c.name),
|
|
196
|
+
colorBy: colorColumn?.name,
|
|
197
|
+
colorMap: colorColumn?.colorMap
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (plots.length == 0) {
|
|
201
|
+
return { nodata: true };
|
|
191
202
|
}
|
|
203
|
+
return { plots };
|
|
192
204
|
};
|
|
193
205
|
}
|
|
194
206
|
function validateGeneExpressionNative(G) {
|