@sjcrh/proteinpaint-server 2.191.5 → 2.192.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/genome/hg38.base.js +8 -0
- package/package.json +5 -5
- package/routes/termdb.proteome.js +24 -3
- package/routes/termdb.violinBox.js +1 -1
- package/src/app.js +2028 -1038
- package/src/serverconfig.js +0 -2
- package/routes/termdb.descrstats.js +0 -115
- package/routes/termdb.sampleScatter.js +0 -434
- package/routes/termdb.singleCellPlots.js +0 -159
- package/routes/termdb.singlecellSamples.js +0 -312
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { getColors, getCoordinate, calculatePadding, xAxisOffSet, yAxisOffSet } from "#shared";
|
|
2
|
-
import { isSingleCellTerm, SINGLECELL_GENE_EXPRESSION, SINGLECELL_CELLTYPE } from "#shared/terms.js";
|
|
3
|
-
import { createCanvas } from "canvas";
|
|
4
|
-
import { scaleLinear } from "d3-scale";
|
|
5
|
-
import { rgb } from "d3-color";
|
|
6
|
-
import { refColor } from "./termdb.sampleScatter.js";
|
|
7
|
-
function init({ genomes }) {
|
|
8
|
-
return async function(req, res) {
|
|
9
|
-
try {
|
|
10
|
-
const q = req.query;
|
|
11
|
-
if (!q.genome || !q.dslabel) {
|
|
12
|
-
throw new Error("Genome and dataset label are required for termdb/singleCellPlots request.");
|
|
13
|
-
}
|
|
14
|
-
const g = genomes[q.genome];
|
|
15
|
-
if (!g) throw new Error("Invalid genome name");
|
|
16
|
-
const ds = g.datasets[q.dslabel];
|
|
17
|
-
if (!ds) throw new Error("Invalid dataset label");
|
|
18
|
-
if (!ds.queries?.singleCell) throw new Error("No single cell data on this dataset");
|
|
19
|
-
return getSingleCellScatter(req, res, ds);
|
|
20
|
-
} catch (err) {
|
|
21
|
-
console.error(err);
|
|
22
|
-
res.status(500).json({ error: err.message || String(err) });
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
async function getSingleCellScatter(req, res, ds) {
|
|
27
|
-
const q = req.query;
|
|
28
|
-
const { name, sample } = q.singleCellPlot;
|
|
29
|
-
try {
|
|
30
|
-
const tw = q.colorTW;
|
|
31
|
-
if (!tw || !isSingleCellTerm(tw.term))
|
|
32
|
-
throw new Error("colorTW must be provided and be a single cell term for single cell scatter plot");
|
|
33
|
-
const arg = { plots: [name], sample };
|
|
34
|
-
if (tw.term.type == SINGLECELL_GENE_EXPRESSION) arg.gene = tw.term.gene;
|
|
35
|
-
else if (tw.term.type == SINGLECELL_CELLTYPE) arg.colorBy = tw.term.name;
|
|
36
|
-
else throw new Error(`unsupported single cell term type: ${tw.term.type}`);
|
|
37
|
-
const data = await ds.queries.singleCell.data.get(arg);
|
|
38
|
-
const plot = data.plots[0];
|
|
39
|
-
const cells = [...plot.expCells, ...plot.noExpCells];
|
|
40
|
-
const groups = tw.q?.customset?.groups;
|
|
41
|
-
const cat2GrpName = /* @__PURE__ */ new Map();
|
|
42
|
-
if (groups) {
|
|
43
|
-
for (const group of groups) {
|
|
44
|
-
for (const value of Object.values(group.values)) {
|
|
45
|
-
cat2GrpName.set(value.key, group.name);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const samples = [];
|
|
50
|
-
const categoryCounts = /* @__PURE__ */ new Map();
|
|
51
|
-
let xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, geMin = Infinity, geMax = -Infinity;
|
|
52
|
-
let totalCellCount = 0;
|
|
53
|
-
for (const cell of cells) {
|
|
54
|
-
let category = cell.category;
|
|
55
|
-
const groupName = cat2GrpName.get(category);
|
|
56
|
-
if (groupName !== void 0) category = groupName;
|
|
57
|
-
const isHidden = tw?.q?.hiddenValues ? category in tw.q.hiddenValues : false;
|
|
58
|
-
totalCellCount++;
|
|
59
|
-
categoryCounts.set(category, (categoryCounts.get(category) || 0) + 1);
|
|
60
|
-
if (cell.x < xMin) xMin = cell.x;
|
|
61
|
-
if (cell.x > xMax) xMax = cell.x;
|
|
62
|
-
if (cell.y < yMin) yMin = cell.y;
|
|
63
|
-
if (cell.y > yMax) yMax = cell.y;
|
|
64
|
-
if (Number.isFinite(cell.geneExp) && cell.geneExp < geMin) geMin = cell.geneExp;
|
|
65
|
-
if (Number.isFinite(cell.geneExp) && cell.geneExp > geMax) geMax = cell.geneExp;
|
|
66
|
-
if (isHidden) continue;
|
|
67
|
-
samples.push({
|
|
68
|
-
sampleId: cell.cellId,
|
|
69
|
-
x: cell.x,
|
|
70
|
-
y: cell.y,
|
|
71
|
-
z: 0,
|
|
72
|
-
category,
|
|
73
|
-
shape: "Ref",
|
|
74
|
-
hidden: { category: false },
|
|
75
|
-
geneExp: cell.geneExp
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
const colorMap = {};
|
|
79
|
-
if (tw.term.type == SINGLECELL_CELLTYPE) {
|
|
80
|
-
const defaultK2c = getColors(categoryCounts.size);
|
|
81
|
-
const dsTerm = ds.queries.singleCell?.terms ? ds.queries.singleCell.terms.find((t) => t.name == tw.term.name) : void 0;
|
|
82
|
-
for (const [category, count] of categoryCounts) {
|
|
83
|
-
const color = tw.term.values?.[category]?.color || dsTerm?.values?.[category]?.color || defaultK2c(category);
|
|
84
|
-
colorMap[category] = { sampleCount: count, color, key: category };
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const shapeLegend = [["Ref", { sampleCount: totalCellCount, shape: 0, key: "Ref" }]];
|
|
88
|
-
const colorLegend = Object.entries(colorMap);
|
|
89
|
-
const output = {
|
|
90
|
-
range: { xMin, xMax, yMin, yMax, geMin, geMax },
|
|
91
|
-
//There should only be one chart
|
|
92
|
-
result: { Default: { colorLegend, shapeLegend } }
|
|
93
|
-
};
|
|
94
|
-
if (totalCellCount >= q.canvasSettings.cutoff) {
|
|
95
|
-
const { src, canvasWidth, canvasHeight } = await makeCanvas(
|
|
96
|
-
q,
|
|
97
|
-
samples,
|
|
98
|
-
colorMap,
|
|
99
|
-
{ xMin, xMax, yMin, yMax, geMin, geMax },
|
|
100
|
-
tw.term.type
|
|
101
|
-
);
|
|
102
|
-
output.result.Default.src = src;
|
|
103
|
-
output.result.Default.canvasWidth = canvasWidth;
|
|
104
|
-
output.result.Default.canvasHeight = canvasHeight;
|
|
105
|
-
output.result.Default.totalSampleCount = totalCellCount;
|
|
106
|
-
} else {
|
|
107
|
-
output.result.Default.samples = samples;
|
|
108
|
-
}
|
|
109
|
-
res.send(output);
|
|
110
|
-
} catch (e) {
|
|
111
|
-
console.log(e);
|
|
112
|
-
res.send({ error: e.message || e });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async function makeCanvas(q, samples, colorMap, range, termType) {
|
|
116
|
-
const settings = q.canvasSettings;
|
|
117
|
-
const dpr = settings.devicePixelRatio || 1;
|
|
118
|
-
const extraSpaceX = calculatePadding(settings.minXScale, settings.maxXScale, range.xMin, range.xMax);
|
|
119
|
-
const extraSpaceY = calculatePadding(settings.minYScale, settings.maxYScale, range.yMin, range.yMax);
|
|
120
|
-
const width = settings.width + xAxisOffSet + extraSpaceX + 20;
|
|
121
|
-
const height = settings.height + yAxisOffSet + extraSpaceY + 20;
|
|
122
|
-
const canvas = createCanvas(width * dpr, height * dpr);
|
|
123
|
-
const ctx = canvas.getContext("2d");
|
|
124
|
-
if (dpr > 1) ctx.scale(dpr, dpr);
|
|
125
|
-
const xScale = scaleLinear().domain([range.xMin - extraSpaceX, range.xMax + extraSpaceX]).range([xAxisOffSet, settings.width + xAxisOffSet]);
|
|
126
|
-
const yScale = scaleLinear().domain([range.yMax + extraSpaceY, range.yMin - extraSpaceY]).range([yAxisOffSet, settings.height + yAxisOffSet]);
|
|
127
|
-
let colorGenerator;
|
|
128
|
-
if (Number.isFinite(range.geMin) && Number.isFinite(range.geMax)) {
|
|
129
|
-
colorGenerator = scaleLinear().domain([range.geMin, range.geMax]).range([settings.startColor, settings.stopColor]);
|
|
130
|
-
}
|
|
131
|
-
const color = (sample) => {
|
|
132
|
-
if (termType == SINGLECELL_GENE_EXPRESSION) {
|
|
133
|
-
if (!Number.isFinite(sample.geneExp)) return settings.startColor;
|
|
134
|
-
else if (sample.geneExp > range.geMax) return settings.stopColor;
|
|
135
|
-
else return colorGenerator(sample.geneExp);
|
|
136
|
-
}
|
|
137
|
-
return colorMap[sample.category] ? colorMap[sample.category].color : refColor;
|
|
138
|
-
};
|
|
139
|
-
const x = (sample) => {
|
|
140
|
-
const tmp = getCoordinate(sample.x, settings.minXScale, settings.maxXScale);
|
|
141
|
-
return xScale(tmp);
|
|
142
|
-
};
|
|
143
|
-
const y = (sample) => {
|
|
144
|
-
const tmp = getCoordinate(sample.y, settings.minYScale, settings.maxYScale);
|
|
145
|
-
return yScale(tmp);
|
|
146
|
-
};
|
|
147
|
-
for (const sample of samples) {
|
|
148
|
-
const c = rgb(color(sample));
|
|
149
|
-
c.opacity = settings.opacity;
|
|
150
|
-
ctx.fillStyle = c.toString();
|
|
151
|
-
ctx.beginPath();
|
|
152
|
-
ctx.arc(x(sample), y(sample), settings.radius, 0, Math.PI * 2);
|
|
153
|
-
ctx.fill();
|
|
154
|
-
}
|
|
155
|
-
return { src: canvas.toDataURL(), canvasWidth: width, canvasHeight: height };
|
|
156
|
-
}
|
|
157
|
-
export {
|
|
158
|
-
init
|
|
159
|
-
};
|
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { read_file, file_is_readable } from "#src/utils.js";
|
|
4
|
-
import { mayLog } from "#src/helpers.ts";
|
|
5
|
-
import { joinUrl } from "#shared/joinUrl.js";
|
|
6
|
-
import { run_rust } from "@sjcrh/proteinpaint-rust";
|
|
7
|
-
import serverconfig from "#src/serverconfig.js";
|
|
8
|
-
import { validate_query_singleCell_DEgenes } from "../src/routes/termdb.singlecellDEgenes.ts";
|
|
9
|
-
import { gdc_validate_query_singleCell_data } from "#src/mds3.gdc.js";
|
|
10
|
-
import ky from "ky";
|
|
11
|
-
import { SINGLECELL_CELLTYPE } from "#shared/terms.js";
|
|
12
|
-
function init({ genomes }) {
|
|
13
|
-
return async (req, res) => {
|
|
14
|
-
const q = req.query;
|
|
15
|
-
let result;
|
|
16
|
-
try {
|
|
17
|
-
const g = genomes[q.genome];
|
|
18
|
-
if (!g) throw new Error("invalid genome name");
|
|
19
|
-
const ds = g.datasets[q.dslabel];
|
|
20
|
-
if (!ds) throw new Error("invalid dataset name");
|
|
21
|
-
if (!ds.queries?.singleCell) throw new Error("no singlecell data on this dataset");
|
|
22
|
-
result = await ds.queries.singleCell.samples.get(q);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
if (e.stack) console.log(e.stack);
|
|
25
|
-
result = {
|
|
26
|
-
status: e.status || 400,
|
|
27
|
-
error: e.message || e
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
res.send(result);
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
async function validate_query_singleCell(ds, genome) {
|
|
34
|
-
const q = ds.queries.singleCell;
|
|
35
|
-
if (!q) return;
|
|
36
|
-
if (typeof q.samples != "object") throw new Error("singleCell.samples{} not object");
|
|
37
|
-
if (typeof q.data != "object") throw new Error("singleCell.data{} not object");
|
|
38
|
-
if (typeof q.samples.get == "function") {
|
|
39
|
-
} else {
|
|
40
|
-
await validateSamples(q, ds);
|
|
41
|
-
}
|
|
42
|
-
if (q.data.src == "gdcapi") {
|
|
43
|
-
gdc_validate_query_singleCell_data(ds, genome);
|
|
44
|
-
} else if (q.data.src == "native") {
|
|
45
|
-
validateDataNative(q.data, ds);
|
|
46
|
-
} else {
|
|
47
|
-
throw new Error("unknown singleCell.data.src");
|
|
48
|
-
}
|
|
49
|
-
colorColumn2terms(ds.queries.singleCell.data.plots, ds);
|
|
50
|
-
if (q.geneExpression) {
|
|
51
|
-
if (typeof q.geneExpression != "object") throw new Error("singleCell.geneExpression not object");
|
|
52
|
-
if (q.geneExpression.src == "native") {
|
|
53
|
-
validateGeneExpressionNative(q.geneExpression);
|
|
54
|
-
} else if (q.geneExpression.src == "gdcapi") {
|
|
55
|
-
gdc_validateGeneExpression(q.geneExpression, ds, genome);
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error("unknown singleCell.geneExpression.src");
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (q.DEgenes) {
|
|
61
|
-
if (typeof q.DEgenes != "object") throw new Error("singleCell.DEgenes not object");
|
|
62
|
-
validate_query_singleCell_DEgenes(ds);
|
|
63
|
-
}
|
|
64
|
-
if (q.images) {
|
|
65
|
-
if (typeof q.images != "object") throw new Error("singleCell.images not object");
|
|
66
|
-
validateImages(q.images);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function validateImages(images) {
|
|
70
|
-
if (!images.folder) throw new Error("images.folder missing");
|
|
71
|
-
if (!images.label) images.label = "Images";
|
|
72
|
-
if (!images.fileName) throw new Error("images.fileName missing");
|
|
73
|
-
}
|
|
74
|
-
async function validateSamples(q, ds) {
|
|
75
|
-
const S = q.samples, D = q.data;
|
|
76
|
-
const samples = /* @__PURE__ */ new Map();
|
|
77
|
-
for (const plot of D.plots) {
|
|
78
|
-
if (plot.isMetaResult) {
|
|
79
|
-
const sampleName = plot?.sampleId || plot.name.replace(/\s/g, "_");
|
|
80
|
-
const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, sampleName + (plot.fileSuffix || ""));
|
|
81
|
-
try {
|
|
82
|
-
await file_is_readable(tsvfile);
|
|
83
|
-
samples.set(sampleName, { sample: sampleName, isMetaResult: true });
|
|
84
|
-
} catch (e) {
|
|
85
|
-
throw new Error(`meta result data file missing or unreadable: ${sampleName} (${tsvfile}): ${e.message || e}`);
|
|
86
|
-
}
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
for (const fn of await fs.promises.readdir(path.join(serverconfig.tpmasterdir, plot.folder))) {
|
|
90
|
-
let sampleName = fn;
|
|
91
|
-
if (plot.fileSuffix) {
|
|
92
|
-
if (!fn.endsWith(plot.fileSuffix))
|
|
93
|
-
throw new Error(`singlecell.sample file name ${fn} does not end with required suffix ${plot.fileSuffix}`);
|
|
94
|
-
sampleName = fn.split(plot.fileSuffix)[0];
|
|
95
|
-
}
|
|
96
|
-
if (!sampleName) throw new Error(`singlecell.sample: cannot derive sample name from file name ${fn}`);
|
|
97
|
-
const sid = ds.cohort.termdb.q.sampleName2id(sampleName);
|
|
98
|
-
if (sid == void 0) throw new Error(`singlecell.sample: unknown sample name ${sampleName}`);
|
|
99
|
-
samples.set(sid, { sample: sampleName });
|
|
100
|
-
}
|
|
101
|
-
if (!plot.colorColumns || plot.colorColumns.length == 0) continue;
|
|
102
|
-
}
|
|
103
|
-
if (samples.size == 0) throw new Error("no scrna samples found");
|
|
104
|
-
console.log(samples.size, "singleCell samples loaded from " + ds.label);
|
|
105
|
-
if (S.sampleColumns) {
|
|
106
|
-
for (const { termid } of S.sampleColumns) {
|
|
107
|
-
const term = ds.cohort.termdb.q.termjsonByOneid(termid);
|
|
108
|
-
if (!term) throw new Error("unknown termid from singlecell.samples.sampleColumns[]");
|
|
109
|
-
const s2v = await ds.cohort.termdb.q.getAllValues4term(termid);
|
|
110
|
-
for (const [sid, v] of s2v.entries()) {
|
|
111
|
-
if (!samples.has(sid)) continue;
|
|
112
|
-
samples.get(sid)[termid] = term.values?.[v]?.label || v;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
S.get = () => {
|
|
117
|
-
const re = { samples: [...samples.values()] };
|
|
118
|
-
if (q.metaResults) {
|
|
119
|
-
re.metaResults = q.metaResults.map((i) => {
|
|
120
|
-
return { name: i.name };
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
return re;
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
function validateDataNative(D, ds) {
|
|
127
|
-
const nameSet = /* @__PURE__ */ new Set();
|
|
128
|
-
for (const plot of D.plots) {
|
|
129
|
-
if (nameSet.has(plot.name)) throw new Error("duplicate plot.name");
|
|
130
|
-
nameSet.add(plot.name);
|
|
131
|
-
if (!plot.folder) throw new Error("plot.folder missing");
|
|
132
|
-
}
|
|
133
|
-
const file2Lines = {};
|
|
134
|
-
D.get = async (q) => {
|
|
135
|
-
const sampleId = q.sample?.eID || q.sample?.sID;
|
|
136
|
-
if (q.checkPlotAvailability) {
|
|
137
|
-
const plots2 = [];
|
|
138
|
-
for (const plot of D.plots) {
|
|
139
|
-
if (!q.plots.includes(plot.name)) continue;
|
|
140
|
-
if (plot.isMetaResult) {
|
|
141
|
-
const sampleName = plot?.sampleId || plot.name.replace(/\s/g, "_");
|
|
142
|
-
if (sampleName != sampleId) continue;
|
|
143
|
-
}
|
|
144
|
-
const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, sampleId + (plot.fileSuffix || ""));
|
|
145
|
-
try {
|
|
146
|
-
await file_is_readable(tsvfile);
|
|
147
|
-
plots2.push({ name: plot.name });
|
|
148
|
-
} catch (_) {
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
const imgs = ds.queries.singleCell?.images;
|
|
152
|
-
if (imgs) {
|
|
153
|
-
const imgFile = path.join(serverconfig.tpmasterdir, imgs.folder, sampleId, imgs.fileName);
|
|
154
|
-
try {
|
|
155
|
-
await file_is_readable(imgFile);
|
|
156
|
-
plots2.push({ name: imgs?.label || "Image" });
|
|
157
|
-
} catch (_) {
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return { plots: plots2 };
|
|
161
|
-
}
|
|
162
|
-
const plots = [];
|
|
163
|
-
let geneExpMap;
|
|
164
|
-
if (ds.queries.singleCell.geneExpression && q.gene) {
|
|
165
|
-
const sample = q.sample || q.singleCellPlot.sample;
|
|
166
|
-
if (!sample) throw new Error("sample is required for gene expression query");
|
|
167
|
-
geneExpMap = await ds.queries.singleCell.geneExpression.get({ sample, gene: q.gene });
|
|
168
|
-
}
|
|
169
|
-
for (const plot of D.plots) {
|
|
170
|
-
if (!q.plots.includes(plot.name)) continue;
|
|
171
|
-
const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, sampleId + (plot.fileSuffix || ""));
|
|
172
|
-
if (!file2Lines[tsvfile]) {
|
|
173
|
-
await file_is_readable(tsvfile);
|
|
174
|
-
const text = await read_file(tsvfile);
|
|
175
|
-
const lines = text.trim().split("\n");
|
|
176
|
-
let first = true;
|
|
177
|
-
const lines2 = [];
|
|
178
|
-
for (const line of lines) {
|
|
179
|
-
if (first) {
|
|
180
|
-
first = false;
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
lines2.push(line.split(" "));
|
|
184
|
-
}
|
|
185
|
-
file2Lines[tsvfile] = lines2;
|
|
186
|
-
}
|
|
187
|
-
const checkColorBy = typeof q.colorBy == "string" ? q.colorBy : q.colorBy?.[plot.name];
|
|
188
|
-
const colorColumn = plot.colorColumns.find((c) => c.name == checkColorBy) || plot.colorColumns[0];
|
|
189
|
-
const expCells = [];
|
|
190
|
-
const noExpCells = [];
|
|
191
|
-
for (const l of file2Lines[tsvfile]) {
|
|
192
|
-
const cellId = l[0], x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
|
|
193
|
-
const category = l[colorColumn?.index] || "";
|
|
194
|
-
if (!cellId) throw new Error("cell id missing");
|
|
195
|
-
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error("x/y not number");
|
|
196
|
-
const cell = { cellId, x, y, category };
|
|
197
|
-
if (geneExpMap) {
|
|
198
|
-
if (geneExpMap[cellId] !== void 0) {
|
|
199
|
-
cell.geneExp = geneExpMap[cellId];
|
|
200
|
-
expCells.push(cell);
|
|
201
|
-
} else {
|
|
202
|
-
noExpCells.push(cell);
|
|
203
|
-
}
|
|
204
|
-
} else noExpCells.push(cell);
|
|
205
|
-
}
|
|
206
|
-
plots.push({
|
|
207
|
-
name: plot.name,
|
|
208
|
-
expCells,
|
|
209
|
-
noExpCells,
|
|
210
|
-
colorColumns: plot.colorColumns.map((c) => c.name),
|
|
211
|
-
colorBy: colorColumn?.name,
|
|
212
|
-
colorMap: colorColumn?.colorMap
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (plots.length == 0) {
|
|
216
|
-
return { nodata: true };
|
|
217
|
-
}
|
|
218
|
-
return { plots };
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
function validateGeneExpressionNative(G) {
|
|
222
|
-
G.sample2gene2expressionBins = {};
|
|
223
|
-
G.get = async (q) => {
|
|
224
|
-
const h5file = path.join(serverconfig.tpmasterdir, G.folder, (q.sample?.eID || q.sample?.sID) + ".h5");
|
|
225
|
-
await file_is_readable(h5file);
|
|
226
|
-
const query_gene = q.gene;
|
|
227
|
-
if (!query_gene) {
|
|
228
|
-
throw new Error("Gene parameter is undefined");
|
|
229
|
-
}
|
|
230
|
-
const read_hdf5_input_type = { query: [query_gene], hdf5_file: h5file };
|
|
231
|
-
const time1 = Date.now();
|
|
232
|
-
const rust_output = await run_rust("readH5", JSON.stringify(read_hdf5_input_type));
|
|
233
|
-
mayLog("Time taken to query HDF5 file:", Date.now() - time1, "ms");
|
|
234
|
-
const result = JSON.parse(rust_output);
|
|
235
|
-
const out = result.query_output[query_gene]?.samples;
|
|
236
|
-
if (!out) throw new Error(`No expression data for ${query_gene}`);
|
|
237
|
-
return out;
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
function gdc_validateGeneExpression(G, ds, genome) {
|
|
241
|
-
G.sample2gene2expressionBins = {};
|
|
242
|
-
G.get = async (q) => {
|
|
243
|
-
try {
|
|
244
|
-
const fileid = q.sample.eID;
|
|
245
|
-
if (ds.__gdc.scrnaAnalysis2hdf5.size == 0) {
|
|
246
|
-
throw new Error("GDC scRNA file mapping is not cached");
|
|
247
|
-
}
|
|
248
|
-
const hdf5id = ds.__gdc.scrnaAnalysis2hdf5.get(fileid);
|
|
249
|
-
if (!hdf5id) throw new Error("cannot map eID to hdf5 id");
|
|
250
|
-
const aliasLst = genome.genedb.getAliasByName.all(q.gene);
|
|
251
|
-
const gencodeId = aliasLst.find((a) => a?.alias.toUpperCase().startsWith("ENSG"))?.alias;
|
|
252
|
-
if (!gencodeId) throw new Error("cannot map gene symbol to GENCODE");
|
|
253
|
-
const body = {
|
|
254
|
-
gene_ids: [gencodeId],
|
|
255
|
-
file_id: hdf5id
|
|
256
|
-
};
|
|
257
|
-
const { host, headers } = ds.getHostHeaders(q);
|
|
258
|
-
const t = Date.now();
|
|
259
|
-
const response = await ky.post(joinUrl(host.rest, "scrna_seq/gene_expression"), {
|
|
260
|
-
timeout: false,
|
|
261
|
-
headers,
|
|
262
|
-
json: body
|
|
263
|
-
});
|
|
264
|
-
if (!response.ok) throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
|
|
265
|
-
const out = await response.json();
|
|
266
|
-
mayLog("gdc scrna gene exp", q.gene, Date.now() - t);
|
|
267
|
-
const result = out.data[0].cells;
|
|
268
|
-
const data = {};
|
|
269
|
-
for (const r of result) {
|
|
270
|
-
data[r.cell_id] = r.value;
|
|
271
|
-
}
|
|
272
|
-
return data;
|
|
273
|
-
} catch (e) {
|
|
274
|
-
if (e.stack) console.log(e.stack);
|
|
275
|
-
return { error: "GDC scRNAseq gene expression request failed with error: " + (e.message || e) };
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
function colorColumn2terms(plots, ds) {
|
|
280
|
-
const termSet = /* @__PURE__ */ new Set();
|
|
281
|
-
for (const plot of plots) {
|
|
282
|
-
const tmpTerms = plot.colorColumns.map((c) => {
|
|
283
|
-
const baseValues = c.colorMap ? Object.keys(c.colorMap) : [];
|
|
284
|
-
return {
|
|
285
|
-
name: c.name,
|
|
286
|
-
isleaf: true,
|
|
287
|
-
/** Note, term may apply to multiple plots.
|
|
288
|
-
* The plot denotes the data file defined in the ds file,
|
|
289
|
-
* which may be the same or different file paths for
|
|
290
|
-
* all the plots. */
|
|
291
|
-
plot: plot.name,
|
|
292
|
-
type: SINGLECELL_CELLTYPE,
|
|
293
|
-
groupsetting: {},
|
|
294
|
-
values: baseValues.reduce((acc, v) => {
|
|
295
|
-
const alias = c?.aliases?.[v];
|
|
296
|
-
acc[v] = {
|
|
297
|
-
key: v,
|
|
298
|
-
label: alias || v,
|
|
299
|
-
color: c.colorMap?.[v] || "#000000"
|
|
300
|
-
};
|
|
301
|
-
return acc;
|
|
302
|
-
}, {})
|
|
303
|
-
};
|
|
304
|
-
});
|
|
305
|
-
tmpTerms.forEach((term) => termSet.add(term));
|
|
306
|
-
}
|
|
307
|
-
ds.queries.singleCell.terms = [...termSet];
|
|
308
|
-
}
|
|
309
|
-
export {
|
|
310
|
-
init,
|
|
311
|
-
validate_query_singleCell
|
|
312
|
-
};
|