@sjcrh/proteinpaint-server 2.64.0 → 2.66.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.64.0",
3
+ "version": "2.66.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",
@@ -27,7 +27,7 @@
27
27
  "postpack": "./dedupjs.sh",
28
28
  "dedup": "./dedupjs.sh",
29
29
  "//todo": "refactor or deprecate the scripts below",
30
- "pretest": "tsc --esModuleInterop genome/*.ts dataset/*.ts && ./test/pretest.js",
30
+ "pretest": "tsc && ./test/pretest.js",
31
31
  "prepare": "ts-patch install",
32
32
  "pretest:type": "npm run checkers",
33
33
  "pretest:integration": "tsc --esModuleInterop genome/*.ts dataset/*.ts",
package/routes/hicdata.js CHANGED
@@ -12,24 +12,25 @@ const api = {
12
12
  },
13
13
  response: {
14
14
  typeId: "HicdataResponse"
15
- }
16
- /*
15
+ },
17
16
  examples: [
18
- {
19
- request: {
20
- body: {
21
- genome: 'hg38-test',
22
- dslabel: 'TermdbTest',
23
- embedder: 'localhost',
24
- gettermbyid: 'subcohort'
25
- }
26
- },
27
- response: {
28
- header: { status: 200 }
29
- }
30
- }
17
+ {
18
+ request: {
19
+ body: {
20
+ embedder: "localhost",
21
+ url: "https://proteinpaint.stjude.org/ppdemo/hg19/hic/hic_demo.hic",
22
+ matrixType: "observed",
23
+ nmeth: "NONE",
24
+ pos1: "3",
25
+ pos2: "2",
26
+ resolution: 1e6
27
+ }
28
+ },
29
+ response: {
30
+ header: { status: 200 }
31
+ }
32
+ }
31
33
  ]
32
- */
33
34
  },
34
35
  post: {
35
36
  alternativeFor: "get",
@@ -8,11 +8,29 @@ const api = {
8
8
  get: {
9
9
  init,
10
10
  request: {
11
- typeId: "HicdataRequest"
11
+ typeId: "HicGenomeRequest"
12
12
  },
13
13
  response: {
14
- typeId: "HicdataResponse"
15
- }
14
+ typeId: "HicGenomeResponse"
15
+ },
16
+ examples: [
17
+ {
18
+ request: {
19
+ body: {
20
+ chrlst: ["chr1", "chr2"],
21
+ embedder: "localhost",
22
+ url: "https://proteinpaint.stjude.org/ppdemo/hg19/hic/hic_demo.hic",
23
+ matrixType: "observed",
24
+ nmeth: "NONE",
25
+ nochr: true,
26
+ resolution: 25e5
27
+ }
28
+ },
29
+ response: {
30
+ header: { status: 200 }
31
+ }
32
+ }
33
+ ]
16
34
  },
17
35
  post: {
18
36
  alternativeFor: "get",
@@ -34,15 +52,7 @@ function init() {
34
52
  return new Promise((resolve, reject) => {
35
53
  const pos1 = req.query.nochr ? lead.replace("chr", "") : lead;
36
54
  const pos2 = req.query.nochr ? follow.replace("chr", "") : follow;
37
- const par = [
38
- matrixType,
39
- req.query.nmeth || "NONE",
40
- file,
41
- pos1,
42
- pos2,
43
- req.query.isfrag ? "FRAG" : "BP",
44
- req.query.resolution
45
- ];
55
+ const par = [matrixType, req.query.nmeth || "NONE", file, pos1, pos2, "BP", req.query.resolution];
46
56
  const ps = spawn(serverconfig.hicstraw, par);
47
57
  const rl = readline.createInterface({ input: ps.stdout });
48
58
  const items = [];
@@ -61,9 +71,6 @@ function init() {
61
71
  fieldnotnumerical++;
62
72
  return;
63
73
  }
64
- if (req.query.mincutoff != void 0 && v <= req.query.mincutoff) {
65
- return;
66
- }
67
74
  items.push([n1, n2, v]);
68
75
  });
69
76
  data.push({ items, lead, follow });
@@ -1,5 +1,6 @@
1
1
  import { getOrderedLabels } from "#src/termdb.barchart.js";
2
2
  import { getData } from "#src/termdb.matrix.js";
3
+ import { TermTypes } from "#shared/terms.js";
3
4
  const api = {
4
5
  endpoint: "termdb/categories",
5
6
  methods: {
@@ -195,6 +196,8 @@ function getDefaultQ(term, q) {
195
196
  }
196
197
  if (term.type == "geneVariant")
197
198
  return {};
199
+ if (term.type == TermTypes.SINGLECELL_CELLTYPE)
200
+ return {};
198
201
  throw "unknown term type";
199
202
  }
200
203
  export {
@@ -53,8 +53,12 @@ function init({ genomes }) {
53
53
  };
54
54
  }
55
55
  async function getResult(q, ds) {
56
- const type = q.dataType;
57
- const { term2sample2value, byTermId, bySampleId } = await ds.queries[type].get(q);
56
+ let _q = q;
57
+ if (q.dataType == TermTypes.GENE_EXPRESSION) {
58
+ _q = JSON.parse(JSON.stringify(q));
59
+ _q.forClusteringAnalysis = true;
60
+ }
61
+ const { term2sample2value, byTermId, bySampleId } = await ds.queries[q.dataType].get(_q);
58
62
  if (term2sample2value.size == 0)
59
63
  throw "no data";
60
64
  if (term2sample2value.size == 1) {
@@ -191,6 +191,10 @@ function addNonDictionaryQueries(c, ds, genome) {
191
191
  q2.rnaseqGeneCount = true;
192
192
  }
193
193
  if (q.singleCell) {
194
+ const plots = "plots" in q.singleCell.data ? q.singleCell.data.plots.map((p) => ({
195
+ name: p.name,
196
+ colorColumn: p.colorColumn.name
197
+ })) : [];
194
198
  q2.singleCell = {
195
199
  samples: {
196
200
  firstColumnName: q.singleCell.samples.firstColumnName,
@@ -199,7 +203,8 @@ function addNonDictionaryQueries(c, ds, genome) {
199
203
  },
200
204
  data: {
201
205
  sameLegend: q.singleCell.data.sameLegend,
202
- refName: q.singleCell.data.refName
206
+ refName: q.singleCell.data.refName,
207
+ plots
203
208
  }
204
209
  };
205
210
  if (q.singleCell.geneExpression) {
@@ -1,7 +1,7 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { spawn } from "child_process";
4
- import { read_file, get_header_txt } from "#src/utils.js";
3
+ import { read_file } from "#src/utils.js";
4
+ import run_R from "#src/run_R.js";
5
5
  import serverconfig from "#src/serverconfig.js";
6
6
  import { gdc_validate_query_singleCell_samples, gdc_validate_query_singleCell_data } from "#src/mds3.gdc.js";
7
7
  const api = {
@@ -61,7 +61,7 @@ async function validate_query_singleCell(ds, genome) {
61
61
  if (q.data.src == "gdcapi") {
62
62
  gdc_validate_query_singleCell_data(ds, genome);
63
63
  } else if (q.data.src == "native") {
64
- validateDataNative(q.data);
64
+ validateDataNative(q.data, ds);
65
65
  } else {
66
66
  throw "unknown singleCell.data.src";
67
67
  }
@@ -90,7 +90,7 @@ async function validateSamplesNative(S, ds) {
90
90
  return { samples: Object.values(samples) };
91
91
  };
92
92
  }
93
- function validateDataNative(D) {
93
+ function validateDataNative(D, ds) {
94
94
  const nameSet = /* @__PURE__ */ new Set();
95
95
  for (const plot of D.plots) {
96
96
  if (nameSet.has(plot.name))
@@ -100,29 +100,45 @@ function validateDataNative(D) {
100
100
  D.get = async (q) => {
101
101
  try {
102
102
  const plots = [];
103
+ let geneExpMap;
104
+ if (ds.queries.singleCell.geneExpression && q.gene) {
105
+ geneExpMap = await ds.queries.singleCell.geneExpression.get({ sample: q.sample, gene: q.gene });
106
+ }
107
+ const file2Lines = {};
103
108
  for (const plot of D.plots) {
109
+ if (!q.plots.includes(plot.name))
110
+ continue;
104
111
  const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, q.sample + plot.fileSuffix);
105
- try {
106
- await fs.promises.stat(tsvfile);
107
- } catch (e) {
108
- if (e.code == "ENOENT") {
109
- continue;
112
+ if (!file2Lines[tsvfile]) {
113
+ try {
114
+ await fs.promises.stat(tsvfile);
115
+ } catch (e) {
116
+ if (e.code == "ENOENT") {
117
+ continue;
118
+ }
119
+ if (e.code == "EACCES")
120
+ throw "cannot read file, permission denied";
121
+ throw "failed to load sc data file";
110
122
  }
111
- if (e.code == "EACCES")
112
- throw "cannot read file, permission denied";
113
- throw "failed to load sc data file";
123
+ file2Lines[tsvfile] = (await read_file(tsvfile)).trim().split("\n");
114
124
  }
115
- const lines = (await read_file(tsvfile)).trim().split("\n");
125
+ const lines = file2Lines[tsvfile];
116
126
  const cells = [];
117
127
  for (let i = 1; i < lines.length; i++) {
118
128
  const l = lines[i].split(" ");
119
- const cellId = l[0], x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
129
+ const cellId = lines.length > 3 ? l[0] : void 0, x = Number(l[plot.coordsColumns.x]), y = Number(l[plot.coordsColumns.y]);
120
130
  const category = l[plot.colorColumn?.index] || "";
121
131
  if (!cellId)
122
132
  throw "cell id missing";
123
133
  if (!Number.isFinite(x) || !Number.isFinite(y))
124
134
  throw "x/y not number";
125
- cells.push({ cellId, x, y, category });
135
+ const cell = { cellId, x, y, category };
136
+ if (geneExpMap) {
137
+ if (geneExpMap[cellId] !== void 0) {
138
+ cell.geneExp = geneExpMap[cellId];
139
+ }
140
+ }
141
+ cells.push(cell);
126
142
  }
127
143
  plots.push({ name: plot.name, cells, colorBy: plot.colorColumn?.name, colorMap: plot.colorColumn?.colorMap });
128
144
  }
@@ -138,41 +154,25 @@ function validateDataNative(D) {
138
154
  };
139
155
  }
140
156
  function validateGeneExpressionNative(G) {
157
+ G.sample2gene2expressionBins = {};
141
158
  G.get = async (q) => {
142
- const tsvfile = path.join(serverconfig.tpmasterdir, G.folder, q.sample);
159
+ const rdsfile = path.join(serverconfig.tpmasterdir, G.folder, q.sample + ".rds");
143
160
  try {
144
- await fs.promises.stat(tsvfile);
161
+ await fs.promises.stat(rdsfile);
145
162
  } catch (e) {
146
- throw "geneExp matrix file not found or readable for this sample";
163
+ return {};
147
164
  }
148
- const header = await get_header_txt(tsvfile);
149
- return await grepMatrix4geneExpression(tsvfile, q.gene, header);
165
+ let out;
166
+ try {
167
+ out = JSON.parse(
168
+ await run_R(path.join(serverconfig.binpath, "utils", "getGeneFromMatrix.R"), null, [rdsfile, q.gene])
169
+ );
170
+ } catch (e) {
171
+ return {};
172
+ }
173
+ return out;
150
174
  };
151
175
  }
152
- function grepMatrix4geneExpression(tsvfile, gene, header) {
153
- return new Promise((resolve, reject) => {
154
- const cp = spawn("grep", ["-m", "1", gene + " ", tsvfile]);
155
- const out = [], err = [];
156
- cp.stdout.on("data", (d) => out.push(d));
157
- cp.stderr.on("data", (d) => err.push(d));
158
- cp.on("close", () => {
159
- const e = err.join("");
160
- if (e)
161
- reject(e);
162
- const l = out.join("").split(" ");
163
- if (l.length != header.length)
164
- reject(`number of fields differ between data line and header: ${l.length} ${header.length}`);
165
- const cell2value = {};
166
- for (let i = 1; i < l.length; i++) {
167
- const v = Number(l[i]);
168
- if (Number.isNaN(v))
169
- continue;
170
- cell2value[header[i]] = v;
171
- }
172
- resolve(cell2value);
173
- });
174
- });
175
- }
176
176
  export {
177
177
  api,
178
178
  validate_query_singleCell
@@ -1,9 +1,9 @@
1
- import { gdcGetCasesWithExpressionDataFromCohort } from "../src/mds3.gdc.js";
2
1
  import path from "path";
3
2
  import { run_rust } from "@sjcrh/proteinpaint-rust";
4
3
  import got from "got";
5
4
  import serverconfig from "#src/serverconfig.js";
6
5
  import { get_samples } from "#src/termdb.sql.js";
6
+ import { makeFilter } from "#src/mds3.gdc.js";
7
7
  const api = {
8
8
  endpoint: "termdb/topVariablyExpressedGenes",
9
9
  methods: {
@@ -106,21 +106,20 @@ async function computeGenes4nativeDs(q, ds, matrixFile, samples) {
106
106
  function gdcValidateQuery(ds, genome) {
107
107
  ds.queries.topVariablyExpressedGenes.getGenes = async (q) => {
108
108
  if (serverconfig.features.gdcGenes) {
109
- console.log("!!GDC!! using serverconfig.features.gdcGenes[]");
109
+ console.log(
110
+ "!!GDC!! using serverconfig.features.gdcGenes[] but not live api query. only use this on DEV and never on PROD!"
111
+ );
110
112
  return serverconfig.features.gdcGenes;
111
113
  }
112
- if (!ds.__gdc.doneCaching)
114
+ if (!ds.__gdc.doneCaching) {
113
115
  throw "The server has not finished caching the case IDs: try again in about 2 minutes.";
114
- const caseLst = await gdcGetCasesWithExpressionDataFromCohort(q, ds);
115
- if (caseLst.length == 0) {
116
- return [];
117
116
  }
118
117
  const { host, headers } = ds.getHostHeaders(q);
119
118
  const url = path.join(host.geneExp, "/gene_expression/gene_selection");
120
119
  try {
121
120
  const response = await got.post(url, {
122
121
  headers,
123
- body: JSON.stringify(getGeneSelectionArg(q, caseLst))
122
+ body: JSON.stringify(getGeneSelectionArg(q))
124
123
  });
125
124
  const re = JSON.parse(response.body);
126
125
  const genes = [];
@@ -143,13 +142,14 @@ function gdcValidateQuery(ds, genome) {
143
142
  throw e;
144
143
  }
145
144
  };
146
- function getGeneSelectionArg(q, caseLst) {
147
- return {
148
- case_ids: caseLst,
149
- //.slice(0, 20),
150
- gene_ids: tempGetCGCgenes(genome),
145
+ function getGeneSelectionArg(q) {
146
+ const arg = {
147
+ // add any to avoid tsc err
148
+ case_filters: makeFilter(q),
151
149
  selection_size: Number(q.maxGenes)
152
150
  };
151
+ arg.gene_ids = tempGetCGCgenes(genome);
152
+ return arg;
153
153
  }
154
154
  }
155
155
  function tempGetCGCgenes(genome) {