@sjcrh/proteinpaint-server 2.170.0 → 2.171.0-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.170.0",
3
+ "version": "2.171.0-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",
@@ -62,11 +62,11 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@sjcrh/augen": "2.143.0",
65
- "@sjcrh/proteinpaint-python": "2.169.0",
65
+ "@sjcrh/proteinpaint-python": "2.171.0-0",
66
66
  "@sjcrh/proteinpaint-r": "2.152.1-0",
67
- "@sjcrh/proteinpaint-rust": "2.170.0",
68
- "@sjcrh/proteinpaint-shared": "2.170.0",
69
- "@sjcrh/proteinpaint-types": "2.170.0",
67
+ "@sjcrh/proteinpaint-rust": "2.171.0-0",
68
+ "@sjcrh/proteinpaint-shared": "2.171.0-0",
69
+ "@sjcrh/proteinpaint-types": "2.171.0-0",
70
70
  "@types/express": "^5.0.0",
71
71
  "@types/express-session": "^1.18.1",
72
72
  "better-sqlite3": "^12.4.1",
package/routes/grin2.js CHANGED
@@ -6,16 +6,7 @@ import { run_rust } from "@sjcrh/proteinpaint-rust";
6
6
  import { mayLog } from "#src/helpers.ts";
7
7
  import { get_samples } from "#src/termdb.sql.js";
8
8
  import { read_file, file_is_readable } from "#src/utils.js";
9
- import {
10
- dtsnvindel,
11
- dtcnv,
12
- dtfusionrna,
13
- dtsv,
14
- dt2lesion,
15
- optionToDt,
16
- formatElapsedTime,
17
- MANHATTAN_LOG_QVALUE_CUTOFF
18
- } from "#shared";
9
+ import { dtsnvindel, dtcnv, dtfusionrna, dtsv, dt2lesion, optionToDt, formatElapsedTime } from "#shared";
19
10
  import crypto from "crypto";
20
11
  const MAX_LESIONS_PER_TYPE = serverconfig.features.grin2maxLesionPerType || 11e4;
21
12
  const api = {
@@ -115,9 +106,6 @@ async function runGrin2(g, ds, request) {
115
106
  lesionTypeMap: buildLesionTypeMap(availableDataTypes)
116
107
  };
117
108
  for (const c in g.majorchr) {
118
- if (ds.queries.singleSampleMutation.discoPlot?.skipChrM) {
119
- if (c.toLowerCase() == "chrm") continue;
120
- }
121
109
  pyInput.chromosomelist[c] = g.majorchr[c];
122
110
  }
123
111
  const grin2AnalysisStart = Date.now();
@@ -141,7 +129,9 @@ async function runGrin2(g, ds, request) {
141
129
  png_dot_radius: request.pngDotRadius,
142
130
  lesion_type_colors: request.lesionTypeColors,
143
131
  q_value_threshold: request.qValueThreshold,
144
- log_cutoff: MANHATTAN_LOG_QVALUE_CUTOFF
132
+ max_capped_points: request.maxCappedPoints,
133
+ hard_cap: request.hardCap,
134
+ bin_size: request.binSize
145
135
  };
146
136
  const manhattanPlotStart = Date.now();
147
137
  const rsResult = await run_rust("manhattan_plot", JSON.stringify(rustInput));
@@ -214,12 +204,14 @@ async function processSampleData(samples, ds, request, tracker) {
214
204
  await file_is_readable(filepath);
215
205
  const mlst = JSON.parse(await read_file(filepath));
216
206
  const { sampleLesions, contributedTypes } = await processSampleMlst(sample.name, mlst, request, tracker);
217
- lesions.push(...sampleLesions);
207
+ const skipChrM = ds.queries.singleSampleMutation.discoPlot?.skipChrM;
208
+ const filteredLesions = skipChrM ? sampleLesions.filter((lesion) => lesion[1].toLowerCase() !== "chrm") : sampleLesions;
209
+ lesions.push(...filteredLesions);
218
210
  for (const type of contributedTypes) {
219
211
  samplesPerType.get(type)?.add(sample.name);
220
212
  }
221
213
  processingSummary.processedSamples += 1;
222
- processingSummary.totalLesions += sampleLesions.length;
214
+ processingSummary.totalLesions += filteredLesions.length;
223
215
  if (allTypesCapped(tracker)) {
224
216
  const remaining = samples.length - 1 - i;
225
217
  if (remaining > 0) processingSummary.unprocessedSamples += remaining;
@@ -9,7 +9,8 @@ const api = {
9
9
  }
10
10
  }
11
11
  };
12
- function init({ genomes }) {
12
+ function init(arg) {
13
+ const genomes = arg.genomes;
13
14
  return async (req, res) => {
14
15
  try {
15
16
  const q = req.query;
@@ -5,6 +5,7 @@ import { sortPlot2Values } from "./termdb.violin.ts";
5
5
  import { getDescrStats, getStdDev, getMean } from "./termdb.descrstats.ts";
6
6
  import { run_rust } from "@sjcrh/proteinpaint-rust";
7
7
  import { roundValueAuto } from "#shared/roundValue.js";
8
+ import { isNumericTerm } from "@sjcrh/proteinpaint-shared";
8
9
  const api = {
9
10
  endpoint: "termdb/boxplot",
10
11
  methods: {
@@ -31,13 +32,11 @@ function init({ genomes }) {
31
32
  try {
32
33
  const data = await getData({ filter: q.filter, filter0: q.filter0, terms, __protected__: q.__protected__ }, ds);
33
34
  if (data.error) throw new Error(data.error);
34
- const { absMin, absMax, charts, uncomputableValues, descrStats, outlierMin, outlierMax } = await processData(
35
- data,
36
- q
37
- );
35
+ const { absMin, absMax, bins, charts, uncomputableValues, descrStats, outlierMin, outlierMax } = await processData(data, q);
38
36
  const returnData = {
39
37
  absMin: q.removeOutliers ? outlierMin : absMin,
40
38
  absMax: q.removeOutliers ? outlierMax : absMax,
39
+ bins,
41
40
  charts,
42
41
  uncomputableValues: setUncomputableValues(uncomputableValues),
43
42
  descrStats
@@ -54,15 +53,15 @@ async function processData(data, q) {
54
53
  const values = samples.map((s) => s?.[q.tw.$id]?.value).filter((v) => typeof v === "number" && !q.tw.term.values?.[v]?.uncomputable);
55
54
  const descrStats = getDescrStats(values, q.removeOutliers);
56
55
  const sampleType = `All ${data.sampleType?.plural_name || "samples"}`;
57
- const overlayTerm = q.overlayTw;
58
- const divideTerm = q.divideTw;
56
+ const overlayTw = q.overlayTw;
57
+ const divideTw = q.divideTw;
59
58
  const { absMin, absMax, chart2plot2values, uncomputableValues } = parseValues(
60
59
  q,
61
60
  data,
62
61
  sampleType,
63
62
  q.isLogScale,
64
- overlayTerm,
65
- divideTerm
63
+ overlayTw,
64
+ divideTw
66
65
  );
67
66
  if (!absMin && absMin !== 0) throw new Error("absMin is undefined");
68
67
  if (!absMax && absMax !== 0) throw new Error("absMax is undefined");
@@ -70,7 +69,7 @@ async function processData(data, q) {
70
69
  let outlierMin = Number.POSITIVE_INFINITY, outlierMax = Number.NEGATIVE_INFINITY;
71
70
  for (const [chart, plot2values] of chart2plot2values) {
72
71
  const plots = [];
73
- for (const [key, values2] of sortPlot2Values(data, plot2values, overlayTerm)) {
72
+ for (const [key, values2] of sortPlot2Values(data, plot2values, overlayTw)) {
74
73
  ;
75
74
  [outlierMax, outlierMin] = setPlotData(
76
75
  plots,
@@ -79,14 +78,14 @@ async function processData(data, q) {
79
78
  sampleType,
80
79
  descrStats,
81
80
  q,
82
- data,
83
81
  outlierMin,
84
82
  outlierMax,
85
- overlayTerm
83
+ overlayTw
86
84
  );
87
85
  }
88
86
  if (q.tw.term?.values) setHiddenPlots(q.tw, plots);
89
- if (overlayTerm && overlayTerm.term?.values) setHiddenPlots(overlayTerm, plots);
87
+ if (overlayTw && overlayTw.term?.values) setHiddenPlots(overlayTw, plots);
88
+ if (divideTw && divideTw.term?.values) setHiddenPlots(divideTw, plots);
90
89
  if (q.orderByMedian == true) {
91
90
  plots.sort((a, b) => a.boxplot.p50 - b.boxplot.p50);
92
91
  }
@@ -96,11 +95,16 @@ async function processData(data, q) {
96
95
  }, 0);
97
96
  charts[chart] = { chartId: chart, plots, sampleCount };
98
97
  }
99
- if (q.showAssocTests && overlayTerm) await getWilcoxonData(charts);
98
+ const bins = {
99
+ term1: numericBins(q.tw, data)
100
+ };
101
+ if (overlayTw) bins.term2 = numericBins(overlayTw, data);
102
+ if (divideTw) bins.term0 = numericBins(divideTw, data);
103
+ if (q.showAssocTests && overlayTw) await getWilcoxonData(charts);
100
104
  Object.keys(charts).forEach((c) => charts[c].plots.forEach((p) => delete p.tempValues));
101
- return { absMin, absMax, charts, uncomputableValues, descrStats, outlierMin, outlierMax };
105
+ return { absMin, absMax, bins, charts, uncomputableValues, descrStats, outlierMin, outlierMax };
102
106
  }
103
- function setPlotData(plots, values, key, sampleType, descrStats, q, data, outlierMin, outlierMax, overlayTerm) {
107
+ function setPlotData(plots, values, key, sampleType, descrStats, q, outlierMin, outlierMax, overlayTw) {
104
108
  const sortedValues = values.sort((a, b) => a - b);
105
109
  const vs = sortedValues.map((v) => {
106
110
  const value = { value: v };
@@ -112,33 +116,24 @@ function setPlotData(plots, values, key, sampleType, descrStats, q, data, outlie
112
116
  }
113
117
  const boxplot = boxplot_getvalue(vs, q.removeOutliers);
114
118
  if (!boxplot) throw new Error("boxplot_getvalue failed [termdb.boxplot init()]");
115
- const _plot = {
119
+ const plot = {
116
120
  boxplot,
117
121
  descrStats: setIndividualBoxPlotStats(boxplot, sortedValues),
118
122
  //quick fix
119
123
  //to delete later
120
124
  tempValues: sortedValues
121
125
  };
122
- if (overlayTerm) {
123
- const _key = overlayTerm?.term?.values?.[key]?.label || key;
124
- const plotLabel = `${_key}, n=${values.length}`;
125
- const overlayBins = numericBins(overlayTerm, data);
126
- const plot = Object.assign(_plot, {
127
- color: overlayTerm?.term?.values?.[key]?.color || null,
128
- key: _key,
129
- overlayBins: overlayBins.has(key) ? overlayBins.get(key) : null,
130
- seriesId: key
131
- });
132
- plot.boxplot.label = plotLabel;
133
- plots.push(plot);
126
+ if (overlayTw) {
127
+ const _key = overlayTw?.term?.values?.[key]?.label || key;
128
+ plot.color = overlayTw?.term?.values?.[key]?.color || null;
129
+ plot.key = _key;
130
+ plot.seriesId = key;
131
+ plot.boxplot.label = `${_key}, n=${values.length}`;
134
132
  } else {
135
- const plotLabel = `${sampleType}, n=${values.length}`;
136
- const plot = Object.assign(_plot, {
137
- key: sampleType
138
- });
139
- plot.boxplot.label = plotLabel;
140
- plots.push(plot);
133
+ plot.key = sampleType;
134
+ plot.boxplot.label = `${sampleType}, n=${values.length}`;
141
135
  }
136
+ plots.push(plot);
142
137
  return [outlierMax, outlierMin];
143
138
  }
144
139
  function setIndividualBoxPlotStats(boxplot, values) {
@@ -204,7 +199,7 @@ async function getWilcoxonData(charts) {
204
199
  }
205
200
  }
206
201
  }
207
- function parseValues(q, data, sampleType, isLog, overlayTerm, divideTerm) {
202
+ function parseValues(q, data, sampleType, isLog, overlayTw, divideTw) {
208
203
  const chart2plot2values = /* @__PURE__ */ new Map();
209
204
  const uncomputableValues = {};
210
205
  let absMin = Infinity, absMax = -Infinity;
@@ -219,20 +214,20 @@ function parseValues(q, data, sampleType, isLog, overlayTerm, divideTerm) {
219
214
  if (isLog && value.value <= 0) continue;
220
215
  let chart = "";
221
216
  let plot = sampleType;
222
- if (divideTerm) {
223
- if (!val[divideTerm?.$id]) continue;
224
- const value0 = val[divideTerm.$id];
225
- if (divideTerm.term?.values?.[value0.key]?.uncomputable) {
226
- const label = divideTerm.term.values[value0?.key]?.label;
217
+ if (divideTw) {
218
+ if (!val[divideTw?.$id]) continue;
219
+ const value0 = val[divideTw.$id];
220
+ if (divideTw.term?.values?.[value0.key]?.uncomputable) {
221
+ const label = divideTw.term.values[value0?.key]?.label;
227
222
  uncomputableValues[label] = (uncomputableValues[label] || 0) + 1;
228
223
  }
229
224
  chart = value0.key;
230
225
  }
231
- if (overlayTerm) {
232
- if (!val[overlayTerm?.$id]) continue;
233
- const value2 = val[overlayTerm.$id];
234
- if (overlayTerm.term?.values?.[value2.key]?.uncomputable) {
235
- const label = overlayTerm.term.values[value2?.key]?.label;
226
+ if (overlayTw) {
227
+ if (!val[overlayTw?.$id]) continue;
228
+ const value2 = val[overlayTw.$id];
229
+ if (overlayTw.term?.values?.[value2.key]?.uncomputable) {
230
+ const label = overlayTw.term.values[value2?.key]?.label;
236
231
  uncomputableValues[label] = (uncomputableValues[label] || 0) + 1;
237
232
  }
238
233
  plot = value2.key;
@@ -247,9 +242,13 @@ function parseValues(q, data, sampleType, isLog, overlayTerm, divideTerm) {
247
242
  }
248
243
  return { absMax, absMin, chart2plot2values, uncomputableValues };
249
244
  }
250
- function numericBins(overlayTerm, data) {
251
- const overlayBins = data.refs.byTermId[overlayTerm?.$id]?.bins ?? [];
252
- return new Map(overlayBins.map((bin) => [bin.label, bin]));
245
+ function numericBins(tw, data) {
246
+ const bins = {};
247
+ if (!isNumericTerm(tw?.term)) return bins;
248
+ for (const bin of data.refs.byTermId[tw?.$id]?.bins || []) {
249
+ bins[bin.label] = bin;
250
+ }
251
+ return bins;
253
252
  }
254
253
  export {
255
254
  api,
@@ -150,6 +150,7 @@ function sortPlot2Values(data, plot2values, overlayTerm) {
150
150
  function setResponse(valuesObject, data, q) {
151
151
  const charts = {};
152
152
  const overlayTerm = q.overlayTw;
153
+ const divideTw = q.divideTw;
153
154
  for (const [chart, plot2values] of valuesObject.chart2plot2values) {
154
155
  const plots = [];
155
156
  for (const [plot, values] of sortPlot2Values(data, plot2values, overlayTerm)) {
@@ -157,16 +158,23 @@ function setResponse(valuesObject, data, q) {
157
158
  label: overlayTerm?.term?.values?.[plot]?.label || plot,
158
159
  values,
159
160
  seriesId: plot,
161
+ chartId: chart,
162
+ //quick fix to get list samples working
160
163
  plotValueCount: values?.length,
161
- color: overlayTerm?.term?.values?.[plot]?.color || null,
162
- overlayTwBins: isNumericTerm(overlayTerm?.term) ? numericBins(overlayTerm, data) : null
164
+ color: overlayTerm?.term?.values?.[plot]?.color || null
163
165
  });
164
166
  }
165
167
  charts[chart] = { chartId: chart, plots };
166
168
  }
169
+ const bins = {
170
+ term1: numericBins(q.tw, data)
171
+ };
172
+ if (overlayTerm) bins.term2 = numericBins(overlayTerm, data);
173
+ if (divideTw) bins.term0 = numericBins(divideTw, data);
167
174
  const result = {
168
175
  min: valuesObject.min,
169
176
  max: valuesObject.max,
177
+ bins,
170
178
  charts,
171
179
  uncomputableValues: Object.keys(valuesObject.uncomputableValues).length > 0 ? valuesObject.uncomputableValues : null
172
180
  };