@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.
Files changed (3) hide show
  1. package/package.json +3 -3
  2. package/routes/grin2.js +21 -134
  3. 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.0",
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.0",
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.0",
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 { lesionData, processingSummary } = await processSampleData(samples, ds, request);
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 (lesionData.length === 0) {
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(lesionData)
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 ${lesionData.length.toLocaleString()} lesions for analysis`);
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, sampleDataTypeCounts } = await processSampleMlst(sample.name, mlst, lesionId, request);
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
- lesionData: lesions,
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
- const opts = {
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
- const chromosome = entry.chromosome || entry.chr;
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 (position === void 0 || position === null || position === "undefined") {
200
+ if (!Number.isInteger(entry.pos)) {
262
201
  return null;
263
202
  }
264
- const numPosition = parseInt(String(position));
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
- const opts = {
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
- if (start === void 0 || start === null || start === "undefined") {
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 (end === void 0 || end === null || end === "undefined") {
213
+ const lesionType = isGain ? "gain" : "loss";
214
+ if (!Number.isInteger(entry.start)) {
306
215
  return null;
307
216
  }
308
- const numStart = parseInt(String(start));
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
- const lesionType = numLog2Ratio >= opts.gainThreshold ? "gain" : "loss";
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 normalizeChromosome(chrom) {
334
- if (!chrom)
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