@sjcrh/proteinpaint-server 2.135.1 → 2.135.2-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 +5 -5
- package/routes/gdc.grin2.run.js +48 -128
- package/routes/profileScores.js +1 -1
- package/src/app.js +117 -156
- package/src/mds3.gdc.filter.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.135.
|
|
3
|
+
"version": "2.135.2-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.135.0",
|
|
64
|
+
"@sjcrh/proteinpaint-python": "2.135.2-0",
|
|
65
65
|
"@sjcrh/proteinpaint-r": "2.130.0",
|
|
66
|
-
"@sjcrh/proteinpaint-rust": "2.135.0",
|
|
67
|
-
"@sjcrh/proteinpaint-shared": "2.135.
|
|
68
|
-
"@sjcrh/proteinpaint-types": "2.135.0",
|
|
66
|
+
"@sjcrh/proteinpaint-rust": "2.135.2-0",
|
|
67
|
+
"@sjcrh/proteinpaint-shared": "2.135.2-0",
|
|
68
|
+
"@sjcrh/proteinpaint-types": "2.135.2-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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { runGRIN2Payload } from "#types/checkers";
|
|
2
|
-
import {
|
|
2
|
+
import { run_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
6
|
import { mayLog } from "#src/helpers.ts";
|
|
7
|
+
const MAX_RECORD = 1e5;
|
|
7
8
|
const api = {
|
|
8
9
|
endpoint: "gdc/runGRIN2",
|
|
9
10
|
methods: {
|
|
@@ -43,12 +44,13 @@ async function runGrin2(genomes, req, res) {
|
|
|
43
44
|
caseFiles: parsedRequest.caseFiles,
|
|
44
45
|
mafOptions: parsedRequest.mafOptions,
|
|
45
46
|
cnvOptions: parsedRequest.cnvOptions,
|
|
46
|
-
chromosomes: []
|
|
47
|
+
chromosomes: [],
|
|
48
|
+
max_record: MAX_RECORD
|
|
47
49
|
};
|
|
48
50
|
const pyInput = {
|
|
49
51
|
genedb: path.join(serverconfig.tpmasterdir, g.genedb.dbfile),
|
|
50
52
|
chromosomelist: {},
|
|
51
|
-
lesion:
|
|
53
|
+
lesion: ""
|
|
52
54
|
};
|
|
53
55
|
for (const c in g.majorchr) {
|
|
54
56
|
if (ds.queries.singleSampleMutation?.discoPlot?.skipChrM) {
|
|
@@ -58,63 +60,33 @@ async function runGrin2(genomes, req, res) {
|
|
|
58
60
|
rustInput.chromosomes.push(c);
|
|
59
61
|
pyInput.chromosomelist[c] = g.majorchr[c];
|
|
60
62
|
}
|
|
61
|
-
let rustOutput = "";
|
|
62
|
-
let buffer = "";
|
|
63
63
|
const downloadStartTime = Date.now();
|
|
64
|
-
const
|
|
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
|
-
}
|
|
64
|
+
const rustOutput = await run_rust("gdcGRIN2", JSON.stringify(rustInput));
|
|
94
65
|
mayLog("[GRIN2] Rust execution completed");
|
|
95
66
|
const downloadTime = Date.now() - downloadStartTime;
|
|
96
67
|
const downloadTimeToPrint = Math.round(downloadTime / 1e3);
|
|
97
|
-
mayLog(`[GRIN2] Rust processing took ${downloadTimeToPrint}`);
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
100
|
-
throw new Error("Failed to process
|
|
68
|
+
mayLog(`[GRIN2] Rust processing took ${downloadTimeToPrint} seconds`);
|
|
69
|
+
const parsedRustResult = parseRustOutput(rustOutput);
|
|
70
|
+
if (!parsedRustResult) {
|
|
71
|
+
throw new Error("Failed to process files: No result from Rust");
|
|
101
72
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
mayLog(`[GRIN2] Extracted ${pyInput.lesion.length} records for python script`);
|
|
73
|
+
if (parsedRustResult.successful_data !== void 0 && parsedRustResult.successful_data !== null) {
|
|
74
|
+
pyInput.lesion = parsedRustResult.successful_data;
|
|
75
|
+
mayLog(`[GRIN2] Extracted ${parsedRustResult.successful_data.length.toLocaleString()} characters for python script`);
|
|
106
76
|
mayLog(
|
|
107
|
-
`[GRIN2] Success: ${parsedRustResult.summary.successful_files}, Failed: ${parsedRustResult.summary.failed_files}`
|
|
77
|
+
`[GRIN2] Success: ${parsedRustResult.summary.successful_files.toLocaleString()}, Failed: ${parsedRustResult.summary.failed_files.toLocaleString()}`
|
|
108
78
|
);
|
|
109
79
|
} else {
|
|
110
|
-
throw "
|
|
80
|
+
throw "No successful data returned from Rust processing";
|
|
111
81
|
}
|
|
112
82
|
const grin2AnalysisStart = Date.now();
|
|
113
|
-
const pyResult = await run_python("
|
|
114
|
-
|
|
83
|
+
const pyResult = await run_python("grin2PpWrapper.py", JSON.stringify(pyInput));
|
|
84
|
+
if (pyResult.stderr?.trim()) {
|
|
85
|
+
mayLog(`[GRIN2] Python stderr: ${pyResult.stderr}`);
|
|
86
|
+
}
|
|
115
87
|
const grin2AnalysisTime = Date.now() - grin2AnalysisStart;
|
|
116
88
|
const grin2AnalysisTimeToPrint = Math.round(grin2AnalysisTime / 1e3);
|
|
117
|
-
mayLog(`[GRIN2] Python processing took ${grin2AnalysisTimeToPrint}`);
|
|
89
|
+
mayLog(`[GRIN2] Python processing took ${grin2AnalysisTimeToPrint} seconds`);
|
|
118
90
|
const resultData = JSON.parse(pyResult);
|
|
119
91
|
const pngImg = resultData.png[0];
|
|
120
92
|
const topGeneTable = resultData.topGeneTable || null;
|
|
@@ -131,88 +103,36 @@ async function runGrin2(genomes, req, res) {
|
|
|
131
103
|
status: "success"
|
|
132
104
|
});
|
|
133
105
|
}
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const MAX_RECORDS = 1e5;
|
|
140
|
-
let totalRecordsProcessed = 0;
|
|
141
|
-
let isCapReached = false;
|
|
142
|
-
for (const line of lines) {
|
|
143
|
-
const trimmedLine = line.trim();
|
|
144
|
-
if (trimmedLine) {
|
|
145
|
-
try {
|
|
146
|
-
const data = JSON.parse(trimmedLine);
|
|
147
|
-
if (data.type === "data") {
|
|
148
|
-
if (isCapReached) {
|
|
149
|
-
mayLog(`[GRIN2] Skipping file ${data.case_id} - record cap of ${MAX_RECORDS} already reached`);
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
const remainingCapacity = MAX_RECORDS - totalRecordsProcessed;
|
|
153
|
-
const incomingRecords = data.data.length;
|
|
154
|
-
let recordsToProcess;
|
|
155
|
-
let recordsProcessedThisFile;
|
|
156
|
-
if (incomingRecords <= remainingCapacity) {
|
|
157
|
-
recordsToProcess = data.data;
|
|
158
|
-
recordsProcessedThisFile = incomingRecords;
|
|
159
|
-
} else {
|
|
160
|
-
recordsToProcess = data.data.slice(0, remainingCapacity);
|
|
161
|
-
recordsProcessedThisFile = remainingCapacity;
|
|
162
|
-
isCapReached = true;
|
|
163
|
-
mayLog(
|
|
164
|
-
`[GRIN2] Record cap reached! Processing only ${recordsProcessedThisFile} of ${incomingRecords} records from file ${data.case_id}`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
processedFiles++;
|
|
168
|
-
allSuccessfulData.push(recordsToProcess);
|
|
169
|
-
totalRecordsProcessed += recordsProcessedThisFile;
|
|
170
|
-
mayLog(
|
|
171
|
-
`[GRIN2] Processed file ${processedFiles}: ${data.case_id} (${data.data_type}) - ${recordsProcessedThisFile} records`
|
|
172
|
-
);
|
|
173
|
-
mayLog(`[GRIN2] Total records processed: ${totalRecordsProcessed}/${MAX_RECORDS}`);
|
|
174
|
-
if (isCapReached) {
|
|
175
|
-
mayLog(`[GRIN2] RECORD CAP REACHED: ${MAX_RECORDS} records processed. Subsequent files will be skipped.`);
|
|
176
|
-
}
|
|
177
|
-
} else if (data.type === "summary") {
|
|
178
|
-
finalSummary = data;
|
|
179
|
-
mayLog(`[GRIN2] Download complete: ${data.successful_files}/${data.total_files} files successful`);
|
|
180
|
-
if (isCapReached) {
|
|
181
|
-
mayLog(`[GRIN2] Processing stopped due to record cap of ${MAX_RECORDS}`);
|
|
182
|
-
mayLog(`[GRIN2] Total records collected: ${totalRecordsProcessed}`);
|
|
183
|
-
}
|
|
184
|
-
if (data.failed_files > 0) {
|
|
185
|
-
mayLog(`[GRIN2] ${data.failed_files} files failed`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
} catch (parseError) {
|
|
189
|
-
console.error("[GRIN2] JSONL parse error:", parseError);
|
|
190
|
-
console.error("[GRIN2] Problematic line:", trimmedLine);
|
|
191
|
-
}
|
|
106
|
+
function parseRustOutput(rustOutput) {
|
|
107
|
+
try {
|
|
108
|
+
const rustResult = JSON.parse(rustOutput);
|
|
109
|
+
if (!rustResult.grin2lesion || !rustResult.summary) {
|
|
110
|
+
throw new Error("Invalid Rust output: missing lesion data or summary");
|
|
192
111
|
}
|
|
112
|
+
mayLog(`[GRIN2] Parsed Rust output successfully`);
|
|
113
|
+
return {
|
|
114
|
+
successful_data: rustResult.grin2lesion,
|
|
115
|
+
failed_files: rustResult.summary.errors || [],
|
|
116
|
+
summary: {
|
|
117
|
+
type: "summary",
|
|
118
|
+
total_files: rustResult.summary.total_files,
|
|
119
|
+
successful_files: rustResult.summary.successful_files,
|
|
120
|
+
failed_files: rustResult.summary.failed_files,
|
|
121
|
+
errors: rustResult.summary.errors || [],
|
|
122
|
+
filtered_records: rustResult.summary.filtered_records || 0,
|
|
123
|
+
filtered_maf_records: rustResult.summary.filtered_maf_records || 0,
|
|
124
|
+
filtered_cnv_records: rustResult.summary.filtered_cnv_records || 0,
|
|
125
|
+
included_maf_records: rustResult.summary.included_maf_records || 0,
|
|
126
|
+
included_cnv_records: rustResult.summary.included_cnv_records || 0,
|
|
127
|
+
filtered_records_by_case: rustResult.summary.filtered_records_by_case || {},
|
|
128
|
+
hyper_mutator_records: rustResult.summary.hyper_mutator_records || {},
|
|
129
|
+
excluded_by_max_record: rustResult.summary.excluded_by_max_record || {},
|
|
130
|
+
skippedChromosomes: rustResult.summary.skipped_chromosomes || {}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new Error(`Failed to parse Rust output: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
193
135
|
}
|
|
194
|
-
if (!finalSummary) {
|
|
195
|
-
throw new Error("No summary found in Rust output");
|
|
196
|
-
}
|
|
197
|
-
return {
|
|
198
|
-
successful_data: allSuccessfulData,
|
|
199
|
-
failed_files: finalSummary.errors || [],
|
|
200
|
-
summary: {
|
|
201
|
-
type: "summary",
|
|
202
|
-
total_files: finalSummary.total_files,
|
|
203
|
-
successful_files: finalSummary.successful_files,
|
|
204
|
-
failed_files: finalSummary.failed_files,
|
|
205
|
-
errors: finalSummary.errors || [],
|
|
206
|
-
filtered_records: finalSummary.filtered_records || 0,
|
|
207
|
-
filtered_maf_records: finalSummary.filtered_maf_records || 0,
|
|
208
|
-
filtered_cnv_records: finalSummary.filtered_cnv_records || 0,
|
|
209
|
-
included_maf_records: finalSummary.included_maf_records || 0,
|
|
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 || {}
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
136
|
}
|
|
217
137
|
export {
|
|
218
138
|
api
|
package/routes/profileScores.js
CHANGED