@sjcrh/proteinpaint-server 2.134.0 → 2.135.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 +72 -1
- package/package.json +5 -5
- package/routes/gdc.grin2.run.js +129 -153
- package/routes/profileFormScores.js +10 -15
- package/routes/profileScores.js +13 -18
- package/src/app.js +802 -746
package/dataset/termdb.test.js
CHANGED
|
@@ -7,7 +7,8 @@ function termdb_test_default() {
|
|
|
7
7
|
isMds3: true,
|
|
8
8
|
isSupportedChartOverride: {
|
|
9
9
|
runChart: () => true,
|
|
10
|
-
frequencyChart: () => true
|
|
10
|
+
frequencyChart: () => true,
|
|
11
|
+
report: () => true
|
|
11
12
|
},
|
|
12
13
|
cohort: {
|
|
13
14
|
massNav: {
|
|
@@ -106,6 +107,76 @@ function termdb_test_default() {
|
|
|
106
107
|
settings: {
|
|
107
108
|
coxDisclaimer: "This is a test disclaimer for the cox regression analysis."
|
|
108
109
|
}
|
|
110
|
+
},
|
|
111
|
+
plotConfigByCohort: {
|
|
112
|
+
default: {
|
|
113
|
+
report: {
|
|
114
|
+
sections: [
|
|
115
|
+
{
|
|
116
|
+
name: "Demographics",
|
|
117
|
+
plots: [
|
|
118
|
+
{
|
|
119
|
+
chartType: "barchart",
|
|
120
|
+
settings: { barchart: { colorBars: true } },
|
|
121
|
+
term: {
|
|
122
|
+
id: "agedx"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
chartType: "barchart",
|
|
127
|
+
term: { id: "sex" },
|
|
128
|
+
settings: { barchart: { colorBars: true } }
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
chartType: "barchart",
|
|
132
|
+
term: { id: "genetic_race" },
|
|
133
|
+
settings: { barchart: { colorBars: true } }
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "Diagnosis",
|
|
139
|
+
plots: [
|
|
140
|
+
{
|
|
141
|
+
chartType: "barchart",
|
|
142
|
+
term: { id: "diaggrp" },
|
|
143
|
+
settings: { barchart: { colorBars: true } }
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "Treatment",
|
|
149
|
+
plots: [
|
|
150
|
+
{
|
|
151
|
+
chartType: "barchart",
|
|
152
|
+
term: { id: "hrtavg" },
|
|
153
|
+
settings: { barchart: { colorBars: true } }
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
chartType: "barchart",
|
|
157
|
+
term: { id: "aaclassic_5" },
|
|
158
|
+
settings: { barchart: { colorBars: true } }
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "Survival",
|
|
164
|
+
plots: [
|
|
165
|
+
{
|
|
166
|
+
chartType: "survival",
|
|
167
|
+
term: { id: "efs" },
|
|
168
|
+
term2: { id: "diaggrp" }
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
chartType: "survival",
|
|
172
|
+
term: { id: "os" },
|
|
173
|
+
term2: { id: "diaggrp" }
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
109
180
|
}
|
|
110
181
|
},
|
|
111
182
|
scatterplots: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.135.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",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@sjcrh/augen": "2.121.0",
|
|
64
|
-
"@sjcrh/proteinpaint-python": "2.
|
|
64
|
+
"@sjcrh/proteinpaint-python": "2.135.0",
|
|
65
65
|
"@sjcrh/proteinpaint-r": "2.130.0",
|
|
66
|
-
"@sjcrh/proteinpaint-rust": "2.
|
|
67
|
-
"@sjcrh/proteinpaint-shared": "2.
|
|
68
|
-
"@sjcrh/proteinpaint-types": "2.
|
|
66
|
+
"@sjcrh/proteinpaint-rust": "2.135.0",
|
|
67
|
+
"@sjcrh/proteinpaint-shared": "2.135.0",
|
|
68
|
+
"@sjcrh/proteinpaint-types": "2.135.0",
|
|
69
69
|
"@types/express": "^5.0.0",
|
|
70
70
|
"@types/express-session": "^1.18.1",
|
|
71
71
|
"better-sqlite3": "^9.4.1",
|
package/routes/gdc.grin2.run.js
CHANGED
|
@@ -3,6 +3,7 @@ import { stream_rust } from "@sjcrh/proteinpaint-rust";
|
|
|
3
3
|
import serverconfig from "#src/serverconfig.js";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { run_python } from "@sjcrh/proteinpaint-python";
|
|
6
|
+
import { mayLog } from "#src/helpers.ts";
|
|
6
7
|
const api = {
|
|
7
8
|
endpoint: "gdc/runGRIN2",
|
|
8
9
|
methods: {
|
|
@@ -16,6 +17,120 @@ const api = {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
};
|
|
20
|
+
function init({ genomes }) {
|
|
21
|
+
return async (req, res) => {
|
|
22
|
+
try {
|
|
23
|
+
await runGrin2(genomes, req, res);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.error("[GRIN2] Error stack:", e.stack);
|
|
26
|
+
const errorResponse = {
|
|
27
|
+
status: "error",
|
|
28
|
+
error: e.message || String(e)
|
|
29
|
+
};
|
|
30
|
+
res.status(500).send(errorResponse);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async function runGrin2(genomes, req, res) {
|
|
35
|
+
const g = genomes.hg38;
|
|
36
|
+
if (!g)
|
|
37
|
+
throw "hg38 missing";
|
|
38
|
+
const ds = g.datasets.GDC;
|
|
39
|
+
if (!ds)
|
|
40
|
+
throw "hg38 GDC missing";
|
|
41
|
+
const parsedRequest = req.query;
|
|
42
|
+
const rustInput = {
|
|
43
|
+
caseFiles: parsedRequest.caseFiles,
|
|
44
|
+
mafOptions: parsedRequest.mafOptions,
|
|
45
|
+
cnvOptions: parsedRequest.cnvOptions,
|
|
46
|
+
chromosomes: []
|
|
47
|
+
};
|
|
48
|
+
const pyInput = {
|
|
49
|
+
genedb: path.join(serverconfig.tpmasterdir, g.genedb.dbfile),
|
|
50
|
+
chromosomelist: {},
|
|
51
|
+
lesion: []
|
|
52
|
+
};
|
|
53
|
+
for (const c in g.majorchr) {
|
|
54
|
+
if (ds.queries.singleSampleMutation?.discoPlot?.skipChrM) {
|
|
55
|
+
if (c.toLowerCase() == "chrm")
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
rustInput.chromosomes.push(c);
|
|
59
|
+
pyInput.chromosomelist[c] = g.majorchr[c];
|
|
60
|
+
}
|
|
61
|
+
let rustOutput = "";
|
|
62
|
+
let buffer = "";
|
|
63
|
+
const downloadStartTime = Date.now();
|
|
64
|
+
const streamResult = stream_rust("gdcGRIN2", JSON.stringify(rustInput), (errors) => {
|
|
65
|
+
if (errors) {
|
|
66
|
+
throw new Error(`Rust process failed: ${errors}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
if (!streamResult) {
|
|
70
|
+
throw new Error("Failed to start Rust streaming process");
|
|
71
|
+
}
|
|
72
|
+
for await (const chunk of streamResult.rustStream) {
|
|
73
|
+
const chunkStr = chunk.toString();
|
|
74
|
+
rustOutput += chunkStr;
|
|
75
|
+
buffer += chunkStr;
|
|
76
|
+
const lines = buffer.split("\n");
|
|
77
|
+
buffer = lines.pop() || "";
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
const trimmedLine = line.trim();
|
|
80
|
+
if (trimmedLine) {
|
|
81
|
+
try {
|
|
82
|
+
const data = JSON.parse(trimmedLine);
|
|
83
|
+
if (data.type === "summary") {
|
|
84
|
+
mayLog(`[GRIN2] Download complete: ${data.successful_files}/${data.total_files} files successful`);
|
|
85
|
+
if (data.failed_files > 0) {
|
|
86
|
+
mayLog(`[GRIN2] ${data.failed_files} files failed`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (_parseError) {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
mayLog("[GRIN2] Rust execution completed");
|
|
95
|
+
const downloadTime = Date.now() - downloadStartTime;
|
|
96
|
+
const downloadTimeToPrint = Math.round(downloadTime / 1e3);
|
|
97
|
+
mayLog(`[GRIN2] Rust processing took ${downloadTimeToPrint}`);
|
|
98
|
+
const rustResult = parseJsonlOutput(rustOutput);
|
|
99
|
+
if (!rustResult) {
|
|
100
|
+
throw new Error("Failed to process MAF files: No result from Rust");
|
|
101
|
+
}
|
|
102
|
+
const parsedRustResult = rustResult;
|
|
103
|
+
if (parsedRustResult.successful_data && Array.isArray(parsedRustResult.successful_data)) {
|
|
104
|
+
pyInput.lesion = parsedRustResult.successful_data.flat();
|
|
105
|
+
mayLog(`[GRIN2] Extracted ${pyInput.lesion.length} records for python script`);
|
|
106
|
+
mayLog(
|
|
107
|
+
`[GRIN2] Success: ${parsedRustResult.summary.successful_files}, Failed: ${parsedRustResult.summary.failed_files}`
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
throw "Unexpected Rust result format";
|
|
111
|
+
}
|
|
112
|
+
const grin2AnalysisStart = Date.now();
|
|
113
|
+
const pyResult = await run_python("gdcGRIN2.py", JSON.stringify(pyInput));
|
|
114
|
+
mayLog(`[GRIN2] Python stderr: ${pyResult.stderr}`);
|
|
115
|
+
const grin2AnalysisTime = Date.now() - grin2AnalysisStart;
|
|
116
|
+
const grin2AnalysisTimeToPrint = Math.round(grin2AnalysisTime / 1e3);
|
|
117
|
+
mayLog(`[GRIN2] Python processing took ${grin2AnalysisTimeToPrint}`);
|
|
118
|
+
const resultData = JSON.parse(pyResult);
|
|
119
|
+
const pngImg = resultData.png[0];
|
|
120
|
+
const topGeneTable = resultData.topGeneTable || null;
|
|
121
|
+
const totalProcessTime = downloadTimeToPrint + grin2AnalysisTimeToPrint;
|
|
122
|
+
return res.json({
|
|
123
|
+
pngImg,
|
|
124
|
+
topGeneTable,
|
|
125
|
+
rustResult: parsedRustResult,
|
|
126
|
+
timing: {
|
|
127
|
+
rustProcessingTime: downloadTimeToPrint,
|
|
128
|
+
grin2ProcessingTime: grin2AnalysisTimeToPrint,
|
|
129
|
+
totalTime: totalProcessTime
|
|
130
|
+
},
|
|
131
|
+
status: "success"
|
|
132
|
+
});
|
|
133
|
+
}
|
|
19
134
|
function parseJsonlOutput(rustOutput) {
|
|
20
135
|
const lines = rustOutput.trim().split("\n");
|
|
21
136
|
const allSuccessfulData = [];
|
|
@@ -31,7 +146,7 @@ function parseJsonlOutput(rustOutput) {
|
|
|
31
146
|
const data = JSON.parse(trimmedLine);
|
|
32
147
|
if (data.type === "data") {
|
|
33
148
|
if (isCapReached) {
|
|
34
|
-
|
|
149
|
+
mayLog(`[GRIN2] Skipping file ${data.case_id} - record cap of ${MAX_RECORDS} already reached`);
|
|
35
150
|
continue;
|
|
36
151
|
}
|
|
37
152
|
const remainingCapacity = MAX_RECORDS - totalRecordsProcessed;
|
|
@@ -45,32 +160,29 @@ function parseJsonlOutput(rustOutput) {
|
|
|
45
160
|
recordsToProcess = data.data.slice(0, remainingCapacity);
|
|
46
161
|
recordsProcessedThisFile = remainingCapacity;
|
|
47
162
|
isCapReached = true;
|
|
48
|
-
|
|
163
|
+
mayLog(
|
|
49
164
|
`[GRIN2] Record cap reached! Processing only ${recordsProcessedThisFile} of ${incomingRecords} records from file ${data.case_id}`
|
|
50
165
|
);
|
|
51
166
|
}
|
|
52
167
|
processedFiles++;
|
|
53
168
|
allSuccessfulData.push(recordsToProcess);
|
|
54
169
|
totalRecordsProcessed += recordsProcessedThisFile;
|
|
55
|
-
|
|
56
|
-
console.log(
|
|
170
|
+
mayLog(
|
|
57
171
|
`[GRIN2] Processed file ${processedFiles}: ${data.case_id} (${data.data_type}) - ${recordsProcessedThisFile} records`
|
|
58
172
|
);
|
|
59
|
-
|
|
173
|
+
mayLog(`[GRIN2] Total records processed: ${totalRecordsProcessed}/${MAX_RECORDS}`);
|
|
60
174
|
if (isCapReached) {
|
|
61
|
-
|
|
62
|
-
`[GRIN2] RECORD CAP REACHED: ${MAX_RECORDS} records processed. Subsequent files will be skipped.`
|
|
63
|
-
);
|
|
175
|
+
mayLog(`[GRIN2] RECORD CAP REACHED: ${MAX_RECORDS} records processed. Subsequent files will be skipped.`);
|
|
64
176
|
}
|
|
65
177
|
} else if (data.type === "summary") {
|
|
66
178
|
finalSummary = data;
|
|
67
|
-
|
|
179
|
+
mayLog(`[GRIN2] Download complete: ${data.successful_files}/${data.total_files} files successful`);
|
|
68
180
|
if (isCapReached) {
|
|
69
|
-
|
|
70
|
-
|
|
181
|
+
mayLog(`[GRIN2] Processing stopped due to record cap of ${MAX_RECORDS}`);
|
|
182
|
+
mayLog(`[GRIN2] Total records collected: ${totalRecordsProcessed}`);
|
|
71
183
|
}
|
|
72
184
|
if (data.failed_files > 0) {
|
|
73
|
-
|
|
185
|
+
mayLog(`[GRIN2] ${data.failed_files} files failed`);
|
|
74
186
|
}
|
|
75
187
|
}
|
|
76
188
|
} catch (parseError) {
|
|
@@ -86,6 +198,7 @@ function parseJsonlOutput(rustOutput) {
|
|
|
86
198
|
successful_data: allSuccessfulData,
|
|
87
199
|
failed_files: finalSummary.errors || [],
|
|
88
200
|
summary: {
|
|
201
|
+
type: "summary",
|
|
89
202
|
total_files: finalSummary.total_files,
|
|
90
203
|
successful_files: finalSummary.successful_files,
|
|
91
204
|
failed_files: finalSummary.failed_files,
|
|
@@ -93,148 +206,11 @@ function parseJsonlOutput(rustOutput) {
|
|
|
93
206
|
filtered_records: finalSummary.filtered_records || 0,
|
|
94
207
|
filtered_maf_records: finalSummary.filtered_maf_records || 0,
|
|
95
208
|
filtered_cnv_records: finalSummary.filtered_cnv_records || 0,
|
|
96
|
-
filtered_records_by_case: finalSummary.filtered_records_by_case || {},
|
|
97
209
|
included_maf_records: finalSummary.included_maf_records || 0,
|
|
98
|
-
included_cnv_records: finalSummary.included_cnv_records || 0
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
function init({ genomes }) {
|
|
103
|
-
return async (req, res) => {
|
|
104
|
-
try {
|
|
105
|
-
console.log("[GRIN2] Validating genome configuration");
|
|
106
|
-
const g = genomes.hg38;
|
|
107
|
-
if (!g)
|
|
108
|
-
throw "hg38 missing";
|
|
109
|
-
const ds = g.datasets.GDC;
|
|
110
|
-
if (!ds)
|
|
111
|
-
throw "hg38 GDC missing";
|
|
112
|
-
console.log(`[GRIN2] Request received:`, JSON.stringify(req.query));
|
|
113
|
-
const parsedRequest = req.query;
|
|
114
|
-
console.log(`[GRIN2] Parsed request: ${JSON.stringify(parsedRequest)}`);
|
|
115
|
-
const rustInput = JSON.stringify({
|
|
116
|
-
caseFiles: parsedRequest.caseFiles,
|
|
117
|
-
mafOptions: parsedRequest.mafOptions,
|
|
118
|
-
cnvOptions: parsedRequest.cnvOptions
|
|
119
|
-
});
|
|
120
|
-
console.log(`[GRIN2] Rust input: ${rustInput}`);
|
|
121
|
-
console.log("[GRIN2] Executing Rust function with streaming...");
|
|
122
|
-
let rustOutput = "";
|
|
123
|
-
let buffer = "";
|
|
124
|
-
const downloadStartTime = Date.now();
|
|
125
|
-
const streamResult = stream_rust("gdcGRIN2", rustInput, (errors) => {
|
|
126
|
-
if (errors) {
|
|
127
|
-
throw new Error(`Rust process failed: ${errors}`);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
if (!streamResult) {
|
|
131
|
-
throw new Error("Failed to start Rust streaming process");
|
|
132
|
-
}
|
|
133
|
-
for await (const chunk of streamResult.rustStream) {
|
|
134
|
-
const chunkStr = chunk.toString();
|
|
135
|
-
rustOutput += chunkStr;
|
|
136
|
-
buffer += chunkStr;
|
|
137
|
-
const lines = buffer.split("\n");
|
|
138
|
-
buffer = lines.pop() || "";
|
|
139
|
-
for (const line of lines) {
|
|
140
|
-
const trimmedLine = line.trim();
|
|
141
|
-
if (trimmedLine) {
|
|
142
|
-
try {
|
|
143
|
-
const data = JSON.parse(trimmedLine);
|
|
144
|
-
if (data.type === "summary") {
|
|
145
|
-
console.log(`[GRIN2] Download complete: ${data.successful_files}/${data.total_files} files successful`);
|
|
146
|
-
if (data.failed_files > 0) {
|
|
147
|
-
console.log(`[GRIN2] ${data.failed_files} files failed`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
} catch (_parseError) {
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
console.log("[GRIN2] Rust execution completed");
|
|
156
|
-
const downloadTime = Date.now() - downloadStartTime;
|
|
157
|
-
const downloadTimeToPrint = Math.round(downloadTime / 1e3);
|
|
158
|
-
console.log(`[GRIN2] Rust processing took ${downloadTimeToPrint}`);
|
|
159
|
-
const rustResult = parseJsonlOutput(rustOutput);
|
|
160
|
-
if (!rustResult) {
|
|
161
|
-
throw new Error("Failed to process MAF files: No result from Rust");
|
|
162
|
-
}
|
|
163
|
-
const parsedRustResult = rustResult;
|
|
164
|
-
let dataForPython = [];
|
|
165
|
-
try {
|
|
166
|
-
if (parsedRustResult.successful_data && Array.isArray(parsedRustResult.successful_data)) {
|
|
167
|
-
dataForPython = parsedRustResult.successful_data.flat();
|
|
168
|
-
console.log(`[GRIN2] Extracted ${dataForPython.length} records for python script`);
|
|
169
|
-
console.log(
|
|
170
|
-
`[GRIN2] Success: ${parsedRustResult.summary.successful_files}, Failed: ${parsedRustResult.summary.failed_files}`
|
|
171
|
-
);
|
|
172
|
-
} else {
|
|
173
|
-
console.warn("[GRIN2] Unexpected Rust result format");
|
|
174
|
-
dataForPython = [];
|
|
175
|
-
}
|
|
176
|
-
} catch (parseError) {
|
|
177
|
-
console.error("[GRIN2] Error processing Rust result:", parseError);
|
|
178
|
-
dataForPython = [];
|
|
179
|
-
}
|
|
180
|
-
const genedbfile = path.join(serverconfig.tpmasterdir, g.genedb.dbfile);
|
|
181
|
-
const pyInput = JSON.stringify({
|
|
182
|
-
genedb: genedbfile,
|
|
183
|
-
chromosomelist: g.majorchr,
|
|
184
|
-
lesion: dataForPython
|
|
185
|
-
// The mutation string from Rust
|
|
186
|
-
});
|
|
187
|
-
console.log("[GRIN2] Executing python script...");
|
|
188
|
-
const grin2AnalysisStart = Date.now();
|
|
189
|
-
let pyResult;
|
|
190
|
-
try {
|
|
191
|
-
pyResult = await run_python("gdcGRIN2.py", pyInput);
|
|
192
|
-
} catch (pyError) {
|
|
193
|
-
console.error("[GRIN2] Python execution failed:", pyError);
|
|
194
|
-
if (pyError && typeof pyError === "object" && "message" in pyError) {
|
|
195
|
-
throw new Error(`Python script failed: ${pyError.message}`);
|
|
196
|
-
} else {
|
|
197
|
-
throw new Error(`Python script failed: ${String(pyError)}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
console.log("[GRIN2] python execution completed");
|
|
201
|
-
console.log(`[GRIN2] Python stderr: ${pyResult.stderr}`);
|
|
202
|
-
const grin2AnalysisTime = Date.now() - grin2AnalysisStart;
|
|
203
|
-
const grin2AnalysisTimeToPrint = Math.round(grin2AnalysisTime / 1e3);
|
|
204
|
-
console.log(`[GRIN2] Python processing took ${grin2AnalysisTimeToPrint}`);
|
|
205
|
-
let resultData;
|
|
206
|
-
try {
|
|
207
|
-
resultData = JSON.parse(pyResult);
|
|
208
|
-
console.log("[GRIN2] Finished python analysis");
|
|
209
|
-
const pngImg = resultData.png[0];
|
|
210
|
-
const topGeneTable = resultData.topGeneTable || null;
|
|
211
|
-
const analysisStats = parsedRustResult.summary || {};
|
|
212
|
-
const totalProcessTime = downloadTimeToPrint + grin2AnalysisTimeToPrint;
|
|
213
|
-
console.log("[GRIN2] Total GRIN2 processing time:", totalProcessTime);
|
|
214
|
-
return res.json({
|
|
215
|
-
pngImg,
|
|
216
|
-
topGeneTable,
|
|
217
|
-
rustResult: parsedRustResult,
|
|
218
|
-
analysisStats,
|
|
219
|
-
timing: {
|
|
220
|
-
rustProcessingTime: downloadTimeToPrint,
|
|
221
|
-
grin2ProcessingTime: grin2AnalysisTimeToPrint,
|
|
222
|
-
totalTime: totalProcessTime
|
|
223
|
-
},
|
|
224
|
-
status: "success"
|
|
225
|
-
});
|
|
226
|
-
} catch (parseError) {
|
|
227
|
-
console.error("[GRIN2] Error parsing python result:", parseError);
|
|
228
|
-
}
|
|
229
|
-
} catch (e) {
|
|
230
|
-
console.error("[GRIN2] Error running analysis:", e);
|
|
231
|
-
console.error("[GRIN2] Error stack:", e.stack);
|
|
232
|
-
const errorResponse = {
|
|
233
|
-
status: "error",
|
|
234
|
-
error: e.message || String(e)
|
|
235
|
-
};
|
|
236
|
-
console.log(`[GRIN2] Sending error response: ${JSON.stringify(errorResponse)}`);
|
|
237
|
-
res.status(500).send(errorResponse);
|
|
210
|
+
included_cnv_records: finalSummary.included_cnv_records || 0,
|
|
211
|
+
filtered_records_by_case: finalSummary.filtered_records_by_case || {},
|
|
212
|
+
hyper_mutator_records: finalSummary.hyper_mutator_records || {},
|
|
213
|
+
skippedChromosomes: finalSummary.skipped_chromosomes || {}
|
|
238
214
|
}
|
|
239
215
|
};
|
|
240
216
|
}
|
|
@@ -35,7 +35,7 @@ async function getScoresDict(query, ds, genome) {
|
|
|
35
35
|
const data = await getData(
|
|
36
36
|
{
|
|
37
37
|
terms,
|
|
38
|
-
filter: query.filter
|
|
38
|
+
filter: query.site || !query.isAggregate ? void 0 : query.filter
|
|
39
39
|
//if isRadarFacility and site is specified, do not apply the filter
|
|
40
40
|
},
|
|
41
41
|
ds,
|
|
@@ -48,19 +48,14 @@ async function getScoresDict(query, ds, genome) {
|
|
|
48
48
|
sites = lst.map((s) => {
|
|
49
49
|
return { label: data.refs.bySampleId[s.sample].label, value: s.sample };
|
|
50
50
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
throw `Invalid user site: ${siteName}`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const site = query.isAggregate ? query.site : userSite;
|
|
63
|
-
const sampleData = data.samples[site] || null;
|
|
51
|
+
let sitesSelected = [];
|
|
52
|
+
if (query.site)
|
|
53
|
+
sitesSelected = [query.site];
|
|
54
|
+
else if (!query.isAggregate)
|
|
55
|
+
sitesSelected = [sites[0].value];
|
|
56
|
+
else
|
|
57
|
+
sitesSelected = query.sites;
|
|
58
|
+
const sampleData = sitesSelected?.length == 1 ? data.samples[sitesSelected[0]] : null;
|
|
64
59
|
const samples = Object.values(data.samples);
|
|
65
60
|
const term2Score = {};
|
|
66
61
|
for (const d of query.scoreTerms) {
|
|
@@ -76,7 +71,7 @@ async function getScoresDict(query, ds, genome) {
|
|
|
76
71
|
term2Score[d.term.id] = percents;
|
|
77
72
|
}
|
|
78
73
|
const hospital = sampleData?.[query.facilityTW.$id]?.value;
|
|
79
|
-
return { term2Score, sites, hospital, n: samples.length };
|
|
74
|
+
return { term2Score, sites, hospital, n: sampleData ? 1 : samples.length };
|
|
80
75
|
}
|
|
81
76
|
function getDict(key, sample) {
|
|
82
77
|
if (!sample[key])
|
package/routes/profileScores.js
CHANGED
|
@@ -29,7 +29,6 @@ function init({ genomes }) {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
async function getScores(query, ds, genome) {
|
|
32
|
-
const isRadarFacility = query.isRadarFacility;
|
|
33
32
|
const terms = [query.facilityTW];
|
|
34
33
|
for (const term of query.scoreTerms) {
|
|
35
34
|
terms.push(term.score);
|
|
@@ -40,8 +39,8 @@ async function getScores(query, ds, genome) {
|
|
|
40
39
|
const data = await getData(
|
|
41
40
|
{
|
|
42
41
|
terms,
|
|
43
|
-
filter:
|
|
44
|
-
//if
|
|
42
|
+
filter: query.site || !query.isAggregate ? void 0 : query.filter
|
|
43
|
+
//if site is specified, do not apply the filter that is for the aggregation
|
|
45
44
|
},
|
|
46
45
|
ds,
|
|
47
46
|
genome
|
|
@@ -56,27 +55,23 @@ async function getScores(query, ds, genome) {
|
|
|
56
55
|
if (query.userSites) {
|
|
57
56
|
sites = sites.filter((s) => query.userSites.includes(s.label));
|
|
58
57
|
}
|
|
59
|
-
let
|
|
60
|
-
if (query.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
throw `Invalid user site: ${siteName}`;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
let site;
|
|
68
|
-
if (isRadarFacility)
|
|
69
|
-
site = query.site;
|
|
58
|
+
let sitesSelected = [];
|
|
59
|
+
if (query.site)
|
|
60
|
+
sitesSelected = [query.site];
|
|
61
|
+
else if (!query.isAggregate)
|
|
62
|
+
sitesSelected = [sites[0].value];
|
|
70
63
|
else
|
|
71
|
-
|
|
72
|
-
const sampleData = data.samples[
|
|
73
|
-
|
|
64
|
+
sitesSelected = query.sites;
|
|
65
|
+
const sampleData = sitesSelected?.length == 1 ? data.samples[sitesSelected[0]] : null;
|
|
66
|
+
let samples = Object.values(data.samples);
|
|
67
|
+
if (sitesSelected?.length > 0)
|
|
68
|
+
samples = samples.filter((s) => sitesSelected.includes(s.sample));
|
|
74
69
|
const term2Score = {};
|
|
75
70
|
for (const d of query.scoreTerms) {
|
|
76
71
|
term2Score[d.score.term.id] = getPercentage(d, samples, sampleData);
|
|
77
72
|
}
|
|
78
73
|
const hospital = sampleData?.[query.facilityTW.$id]?.value;
|
|
79
|
-
return { term2Score, sites, hospital, n: samples.length };
|
|
74
|
+
return { term2Score, sites, hospital, n: sampleData ? 1 : samples.length };
|
|
80
75
|
}
|
|
81
76
|
function getPercentage(d, samples, sampleData) {
|
|
82
77
|
if (!d)
|