@sjcrh/proteinpaint-server 2.139.0 → 2.139.1
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/grin2.js +21 -134
- package/src/app.js +25 -138
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.139.
|
|
3
|
+
"version": "2.139.1",
|
|
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",
|
|
@@ -60,11 +60,11 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@sjcrh/augen": "2.136.0",
|
|
63
|
-
"@sjcrh/proteinpaint-python": "2.139.
|
|
63
|
+
"@sjcrh/proteinpaint-python": "2.139.1",
|
|
64
64
|
"@sjcrh/proteinpaint-r": "2.137.2-0",
|
|
65
65
|
"@sjcrh/proteinpaint-rust": "2.138.3-7",
|
|
66
66
|
"@sjcrh/proteinpaint-shared": "2.138.3-7",
|
|
67
|
-
"@sjcrh/proteinpaint-types": "2.139.
|
|
67
|
+
"@sjcrh/proteinpaint-types": "2.139.1",
|
|
68
68
|
"@types/express": "^5.0.0",
|
|
69
69
|
"@types/express-session": "^1.18.1",
|
|
70
70
|
"better-sqlite3": "^9.4.1",
|
package/routes/grin2.js
CHANGED
|
@@ -60,7 +60,7 @@ async function runGrin2(g, ds, request) {
|
|
|
60
60
|
}
|
|
61
61
|
mayLog("[GRIN2] Processing sample data...");
|
|
62
62
|
const processingStartTime = Date.now();
|
|
63
|
-
const {
|
|
63
|
+
const { lesions, processingSummary } = await processSampleData(samples, ds, request);
|
|
64
64
|
const processingTime = Date.now() - processingStartTime;
|
|
65
65
|
const processingTimeToPrint = Math.round(processingTime / 1e3);
|
|
66
66
|
mayLog(`[GRIN2] Data processing took ${processingTimeToPrint} seconds`);
|
|
@@ -70,13 +70,13 @@ async function runGrin2(g, ds, request) {
|
|
|
70
70
|
if (processingSummary && processingSummary.failedSamples > 0) {
|
|
71
71
|
mayLog(`[GRIN2] Warning: ${processingSummary.failedSamples} samples failed to process`);
|
|
72
72
|
}
|
|
73
|
-
if (
|
|
73
|
+
if (lesions.length === 0) {
|
|
74
74
|
throw new Error("No lesions found after processing all samples. Check filter criteria and input data.");
|
|
75
75
|
}
|
|
76
76
|
const pyInput = {
|
|
77
77
|
genedb: path.join(serverconfig.tpmasterdir, g.genedb.dbfile),
|
|
78
78
|
chromosomelist: {},
|
|
79
|
-
lesion: JSON.stringify(
|
|
79
|
+
lesion: JSON.stringify(lesions)
|
|
80
80
|
};
|
|
81
81
|
for (const c in g.majorchr) {
|
|
82
82
|
if (ds.queries.singleSampleMutation.discoPlot?.skipChrM) {
|
|
@@ -85,7 +85,7 @@ async function runGrin2(g, ds, request) {
|
|
|
85
85
|
}
|
|
86
86
|
pyInput.chromosomelist[c] = g.majorchr[c];
|
|
87
87
|
}
|
|
88
|
-
mayLog(`[GRIN2] Prepared ${
|
|
88
|
+
mayLog(`[GRIN2] Prepared ${lesions.length.toLocaleString()} lesions for analysis`);
|
|
89
89
|
const grin2AnalysisStart = Date.now();
|
|
90
90
|
const pyResult = await run_python("grin2PpWrapper.py", JSON.stringify(pyInput));
|
|
91
91
|
if (pyResult.stderr?.trim()) {
|
|
@@ -126,35 +126,15 @@ async function processSampleData(samples, ds, request) {
|
|
|
126
126
|
failedSamples: 0,
|
|
127
127
|
failedFiles: []
|
|
128
128
|
};
|
|
129
|
-
const dataTypeCounts = {
|
|
130
|
-
[dtsnvindel]: { samples: 0, entries: 0, processedLesions: 0 },
|
|
131
|
-
[dtcnv]: { samples: 0, entries: 0, processedLesions: 0 },
|
|
132
|
-
[dtfusionrna]: { samples: 0, entries: 0, processedLesions: 0 },
|
|
133
|
-
unknown: /* @__PURE__ */ new Map()
|
|
134
|
-
};
|
|
135
129
|
mayLog(`[GRIN2] Processing JSON files for ${samples.length.toLocaleString()} samples`);
|
|
136
130
|
for (const sample of samples) {
|
|
137
131
|
try {
|
|
138
132
|
const filepath = path.join(serverconfig.tpmasterdir, ds.queries.singleSampleMutation.folder, sample.name);
|
|
139
133
|
await file_is_readable(filepath);
|
|
140
134
|
const mlst = JSON.parse(await read_file(filepath));
|
|
141
|
-
const { sampleLesions
|
|
135
|
+
const { sampleLesions } = await processSampleMlst(sample.name, mlst, lesionId, request);
|
|
142
136
|
lesions.push(...sampleLesions);
|
|
143
137
|
lesionId += sampleLesions.length;
|
|
144
|
-
for (const [dt, counts] of Object.entries(sampleDataTypeCounts.known)) {
|
|
145
|
-
if (counts.entries > 0) {
|
|
146
|
-
dataTypeCounts[dt].samples++;
|
|
147
|
-
dataTypeCounts[dt].entries += counts.entries;
|
|
148
|
-
dataTypeCounts[dt].processedLesions += counts.processedLesions;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
for (const [dt, counts] of sampleDataTypeCounts.unknown) {
|
|
152
|
-
if (!dataTypeCounts.unknown.has(dt)) {
|
|
153
|
-
dataTypeCounts.unknown.set(dt, { samples: /* @__PURE__ */ new Set(), entries: 0 });
|
|
154
|
-
}
|
|
155
|
-
dataTypeCounts.unknown.get(dt).samples.add(sample.name);
|
|
156
|
-
dataTypeCounts.unknown.get(dt).entries += counts;
|
|
157
|
-
}
|
|
158
138
|
processingSummary.successfulSamples++;
|
|
159
139
|
} catch (error) {
|
|
160
140
|
processingSummary.failedSamples++;
|
|
@@ -164,176 +144,83 @@ async function processSampleData(samples, ds, request) {
|
|
|
164
144
|
);
|
|
165
145
|
}
|
|
166
146
|
}
|
|
167
|
-
mayLog(`[GRIN2] === DATA TYPE ANALYSIS SUMMARY ===`);
|
|
168
|
-
mayLog(
|
|
169
|
-
`[GRIN2] SNV/Indel (dt=${dtsnvindel}): ${dataTypeCounts[dtsnvindel].samples} samples, ${dataTypeCounts[dtsnvindel].entries} entries, ${dataTypeCounts[dtsnvindel].processedLesions} lesions`
|
|
170
|
-
);
|
|
171
|
-
mayLog(
|
|
172
|
-
`[GRIN2] CNV (dt=${dtcnv}): ${dataTypeCounts[dtcnv].samples} samples, ${dataTypeCounts[dtcnv].entries} entries, ${dataTypeCounts[dtcnv].processedLesions} lesions`
|
|
173
|
-
);
|
|
174
|
-
mayLog(
|
|
175
|
-
`[GRIN2] Fusion (dt=${dtfusionrna}): ${dataTypeCounts[dtfusionrna].samples} samples, ${dataTypeCounts[dtfusionrna].entries} entries, ${dataTypeCounts[dtfusionrna].processedLesions} lesions`
|
|
176
|
-
);
|
|
177
|
-
if (dataTypeCounts.unknown.size > 0) {
|
|
178
|
-
mayLog(`[GRIN2] Unknown data types:`);
|
|
179
|
-
for (const [dt, counts] of dataTypeCounts.unknown) {
|
|
180
|
-
mayLog(`[GRIN2] dt="${dt}": ${counts.samples.size} samples, ${counts.entries} entries`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
147
|
mayLog(`[GRIN2] Total lesions processed: ${lesions.length.toLocaleString()}`);
|
|
184
148
|
return {
|
|
185
|
-
|
|
149
|
+
lesions,
|
|
186
150
|
processingSummary
|
|
187
151
|
};
|
|
188
152
|
}
|
|
189
153
|
async function processSampleMlst(sampleName, mlst, startId, request) {
|
|
190
154
|
const lesions = [];
|
|
191
|
-
const sampleDataTypeCounts = {
|
|
192
|
-
known: {
|
|
193
|
-
[dtsnvindel]: { entries: 0, processedLesions: 0 },
|
|
194
|
-
[dtcnv]: { entries: 0, processedLesions: 0 },
|
|
195
|
-
[dtfusionrna]: { entries: 0, processedLesions: 0 }
|
|
196
|
-
},
|
|
197
|
-
unknown: /* @__PURE__ */ new Map()
|
|
198
|
-
};
|
|
199
155
|
for (const m of mlst) {
|
|
200
156
|
switch (m.dt) {
|
|
201
157
|
case dtsnvindel: {
|
|
202
|
-
sampleDataTypeCounts.known[dtsnvindel].entries++;
|
|
203
158
|
if (!request.snvindelOptions)
|
|
204
159
|
break;
|
|
205
160
|
const snvIndelLesion = filterAndConvertSnvIndel(sampleName, m, request.snvindelOptions);
|
|
206
161
|
if (snvIndelLesion) {
|
|
207
162
|
lesions.push(snvIndelLesion);
|
|
208
|
-
sampleDataTypeCounts.known[dtsnvindel].processedLesions++;
|
|
209
163
|
}
|
|
210
164
|
break;
|
|
211
165
|
}
|
|
212
166
|
case dtcnv: {
|
|
213
|
-
sampleDataTypeCounts.known[dtcnv].entries++;
|
|
214
167
|
if (!request.cnvOptions)
|
|
215
168
|
break;
|
|
216
169
|
const cnvLesion = filterAndConvertCnv(sampleName, m, request.cnvOptions);
|
|
217
170
|
if (cnvLesion) {
|
|
218
171
|
lesions.push(cnvLesion);
|
|
219
|
-
sampleDataTypeCounts.known[dtcnv].processedLesions++;
|
|
220
172
|
}
|
|
221
173
|
break;
|
|
222
174
|
}
|
|
223
175
|
case dtfusionrna: {
|
|
224
|
-
sampleDataTypeCounts.known[dtfusionrna].entries++;
|
|
225
176
|
if (!request.fusionOptions)
|
|
226
177
|
break;
|
|
227
178
|
const fusionLesion = filterAndConvertFusion(sampleName, m, request.fusionOptions);
|
|
228
179
|
if (fusionLesion) {
|
|
229
180
|
lesions.push(fusionLesion);
|
|
230
|
-
sampleDataTypeCounts.known[dtfusionrna].processedLesions++;
|
|
231
181
|
}
|
|
232
182
|
break;
|
|
233
183
|
}
|
|
234
184
|
default: {
|
|
235
|
-
const currentCount = sampleDataTypeCounts.unknown.get(m.dt) || 0;
|
|
236
|
-
sampleDataTypeCounts.unknown.set(m.dt, currentCount + 1);
|
|
237
185
|
break;
|
|
238
186
|
}
|
|
239
187
|
}
|
|
240
188
|
}
|
|
241
189
|
return {
|
|
242
|
-
sampleLesions: lesions
|
|
243
|
-
sampleDataTypeCounts
|
|
190
|
+
sampleLesions: lesions
|
|
244
191
|
};
|
|
245
192
|
}
|
|
246
193
|
function filterAndConvertSnvIndel(sampleName, entry, options) {
|
|
247
|
-
|
|
248
|
-
minTotalDepth: options?.minTotalDepth ?? 10,
|
|
249
|
-
minAltAlleleCount: options?.minAltAlleleCount ?? 2,
|
|
250
|
-
consequences: options?.consequences ?? [],
|
|
251
|
-
hyperMutator: options?.hyperMutator ?? 1e3
|
|
252
|
-
};
|
|
253
|
-
if (opts.consequences.length > 0 && entry.class && !opts.consequences.includes(entry.class)) {
|
|
194
|
+
if (!options?.consequences) {
|
|
254
195
|
return null;
|
|
255
196
|
}
|
|
256
|
-
|
|
257
|
-
const position = entry.start || entry.position || entry.pos;
|
|
258
|
-
if (!chromosome) {
|
|
197
|
+
if (options.consequences.length > 0 && entry.class && !options.consequences.includes(entry.class)) {
|
|
259
198
|
return null;
|
|
260
199
|
}
|
|
261
|
-
if (
|
|
200
|
+
if (!Number.isInteger(entry.pos)) {
|
|
262
201
|
return null;
|
|
263
202
|
}
|
|
264
|
-
|
|
265
|
-
if (isNaN(numPosition)) {
|
|
266
|
-
return null;
|
|
267
|
-
}
|
|
268
|
-
const endPosition = entry.end || position;
|
|
269
|
-
const numEndPosition = parseInt(String(endPosition));
|
|
270
|
-
if (isNaN(numEndPosition)) {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
return [sampleName, normalizeChromosome(chromosome), String(numPosition), String(numEndPosition), "mutation"];
|
|
203
|
+
return [sampleName, entry.chr, entry.pos, entry.pos, "mutation"];
|
|
274
204
|
}
|
|
275
205
|
function filterAndConvertCnv(sampleName, entry, options) {
|
|
276
|
-
|
|
277
|
-
lossThreshold: options?.lossThreshold ?? -0.4,
|
|
278
|
-
gainThreshold: options?.gainThreshold ?? 0.3,
|
|
279
|
-
maxSegLength: options?.maxSegLength ?? 0,
|
|
280
|
-
minSegLength: options?.minSegLength ?? 0,
|
|
281
|
-
hyperMutator: options?.hyperMutator ?? 500
|
|
282
|
-
};
|
|
283
|
-
const log2Ratio = entry.log2Ratio ?? entry.value ?? entry.ratio;
|
|
284
|
-
if (log2Ratio === void 0 || log2Ratio === null) {
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
const numLog2Ratio = parseFloat(String(log2Ratio));
|
|
288
|
-
if (isNaN(numLog2Ratio)) {
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
const isGain = numLog2Ratio >= opts.gainThreshold;
|
|
292
|
-
const isLoss = numLog2Ratio <= opts.lossThreshold;
|
|
293
|
-
if (!isGain && !isLoss) {
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
const chromosome = entry.chromosome || entry.chr;
|
|
297
|
-
const start = entry.start || entry.begin;
|
|
298
|
-
const end = entry.end || entry.stop;
|
|
299
|
-
if (!chromosome) {
|
|
206
|
+
if (!options || options.gainThreshold === void 0 || options.lossThreshold === void 0) {
|
|
300
207
|
return null;
|
|
301
208
|
}
|
|
302
|
-
|
|
209
|
+
const isGain = entry.value >= options.gainThreshold;
|
|
210
|
+
const isLoss = entry.value <= options.lossThreshold;
|
|
211
|
+
if (!isGain && !isLoss)
|
|
303
212
|
return null;
|
|
304
|
-
|
|
305
|
-
if (
|
|
213
|
+
const lesionType = isGain ? "gain" : "loss";
|
|
214
|
+
if (!Number.isInteger(entry.start)) {
|
|
306
215
|
return null;
|
|
307
216
|
}
|
|
308
|
-
|
|
309
|
-
const numEnd = parseInt(String(end));
|
|
310
|
-
if (isNaN(numStart) || isNaN(numEnd)) {
|
|
217
|
+
if (!Number.isInteger(entry.stop)) {
|
|
311
218
|
return null;
|
|
312
219
|
}
|
|
313
|
-
|
|
314
|
-
return [sampleName, normalizeChromosome(chromosome), String(numStart), String(numEnd), lesionType];
|
|
315
|
-
}
|
|
316
|
-
function filterAndConvertFusion(sampleName, entry, options) {
|
|
317
|
-
const opts = {
|
|
318
|
-
fusionTypes: options?.fusionTypes ?? ["gene-gene", "gene-intergenic", "readthrough"],
|
|
319
|
-
minConfidence: options?.minConfidence ?? 0.7
|
|
320
|
-
};
|
|
321
|
-
if (entry.fusionType && !opts.fusionTypes.includes(entry.fusionType))
|
|
322
|
-
return null;
|
|
323
|
-
if (entry.confidence && entry.confidence < opts.minConfidence)
|
|
324
|
-
return null;
|
|
325
|
-
return [
|
|
326
|
-
sampleName,
|
|
327
|
-
normalizeChromosome(entry.chromosome || entry.chr),
|
|
328
|
-
String(entry.start || entry.position),
|
|
329
|
-
String(entry.end || entry.position),
|
|
330
|
-
"fusion"
|
|
331
|
-
];
|
|
220
|
+
return [sampleName, entry.chr, entry.start, entry.stop, lesionType];
|
|
332
221
|
}
|
|
333
|
-
function
|
|
334
|
-
|
|
335
|
-
return "chr?";
|
|
336
|
-
return chrom.startsWith("chr") ? chrom : `chr${chrom}`;
|
|
222
|
+
function filterAndConvertFusion(sampleName, entry, _options) {
|
|
223
|
+
return [sampleName, entry.chrA, entry.posA, entry.posB, "fusion"];
|
|
337
224
|
}
|
|
338
225
|
export {
|
|
339
226
|
api
|