@sjcrh/proteinpaint-server 2.65.0 → 2.67.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.65.0",
3
+ "version": "2.67.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,10 +27,10 @@
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
- "pretest:integration": "tsc --esModuleInterop genome/*.ts dataset/*.ts",
33
+ "pretest:integration": "tsc",
34
34
  "test:integration": "echo 'TODO: server integration tests'",
35
35
  "test:tsc": "tsc --esModuleInterop --noEmit --allowImportingTsExtensions ./shared/types/test/*.type.spec.ts"
36
36
  },
@@ -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: {
@@ -78,11 +79,10 @@ function init({ genomes }) {
78
79
  };
79
80
  }
80
81
  async function trigger_getcategories(q, res, tdb, ds, genome) {
81
- const $id = Math.random().toString();
82
- const tw = { $id, term: q.term, q: q.term1_q || getDefaultQ(q.term, q) };
82
+ const $id = q.tw.$id;
83
83
  const arg = {
84
84
  filter: q.filter,
85
- terms: [tw],
85
+ terms: [q.tw],
86
86
  currentGeneNames: q.currentGeneNames,
87
87
  // optional, from mds3 mayAddGetCategoryArgs()
88
88
  rglst: q.rglst
@@ -92,7 +92,7 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
92
92
  if (data.error)
93
93
  throw data.error;
94
94
  const lst = [];
95
- if (q.term.type == "geneVariant") {
95
+ if (q.tw.term.type == "geneVariant" && !q.tw.q.groupsetting.inuse) {
96
96
  const samples = data.samples;
97
97
  const dtClassMap = /* @__PURE__ */ new Map();
98
98
  if (ds.assayAvailability?.byDt) {
@@ -153,15 +153,15 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
153
153
  lst.push({
154
154
  samplecount: count,
155
155
  key,
156
- label: data.refs?.byTermId?.[$id]?.events?.find((e) => e.event === key).label || q.term?.values?.[key]?.label || key
156
+ label: data.refs?.byTermId?.[$id]?.events?.find((e) => e.event === key).label || q.tw.term?.values?.[key]?.label || key
157
157
  });
158
158
  }
159
159
  }
160
160
  const orderedLabels = getOrderedLabels(
161
- q.term,
161
+ q.tw.term,
162
162
  data.refs?.byTermId?.[$id]?.bins || [],
163
163
  data.refs?.byTermId?.[$id]?.events,
164
- q.term1_q
164
+ q.tw.q
165
165
  );
166
166
  if (orderedLabels.length) {
167
167
  lst.sort((a, b) => orderedLabels.indexOf(a.label) - orderedLabels.indexOf(b.label));
@@ -195,6 +195,8 @@ function getDefaultQ(term, q) {
195
195
  }
196
196
  if (term.type == "geneVariant")
197
197
  return {};
198
+ if (term.type == TermTypes.SINGLECELL_CELLTYPE)
199
+ return {};
198
200
  throw "unknown term type";
199
201
  }
200
202
  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) {
@@ -199,12 +199,18 @@ function addNonDictionaryQueries(c, ds, genome) {
199
199
  },
200
200
  data: {
201
201
  sameLegend: q.singleCell.data.sameLegend,
202
- refName: q.singleCell.data.refName
202
+ refName: q.singleCell.data.refName,
203
+ plots: q.singleCell.data.plots.map((p) => {
204
+ return { name: p.name, colorColumn: p.colorColumn.name };
205
+ })
203
206
  }
204
207
  };
205
208
  if (q.singleCell.geneExpression) {
206
209
  q2.singleCell.geneExpression = {};
207
210
  }
211
+ if (q.singleCell.DEgenes) {
212
+ q2.singleCell.DEgenes = { columnName: q.singleCell.DEgenes.columnName };
213
+ }
208
214
  }
209
215
  if (q.images) {
210
216
  q2.images = {};
@@ -0,0 +1,51 @@
1
+ import { gdc_validate_query_singleCell_DEgenes } from "#src/mds3.gdc.js";
2
+ const api = {
3
+ endpoint: "termdb/singlecellDEgenes",
4
+ methods: {
5
+ all: {
6
+ init,
7
+ request: {
8
+ typeId: "TermdbSinglecellDEgenesRequest"
9
+ },
10
+ response: {
11
+ typeId: "TermdbSinglecellDEgenesResponse"
12
+ }
13
+ }
14
+ }
15
+ };
16
+ function init({ genomes }) {
17
+ return async (req, res) => {
18
+ const q = req.query;
19
+ let result;
20
+ try {
21
+ const g = genomes[q.genome];
22
+ if (!g)
23
+ throw "invalid genome name";
24
+ const ds = g.datasets[q.dslabel];
25
+ if (!ds)
26
+ throw "invalid dataset name";
27
+ if (!ds.queries?.singleCell?.DEgenes)
28
+ throw "not supported on this dataset";
29
+ result = await ds.queries.singleCell.DEgenes.get(q);
30
+ } catch (e) {
31
+ if (e.stack)
32
+ console.log(e.stack);
33
+ result = {
34
+ status: e.status || 400,
35
+ error: e.message || e
36
+ };
37
+ }
38
+ res.send(result);
39
+ };
40
+ }
41
+ async function validate_query_singleCell_DEgenes(ds) {
42
+ if (ds.queries.singleCell.DEgenes.src == "gdcapi") {
43
+ gdc_validate_query_singleCell_DEgenes(ds);
44
+ } else {
45
+ throw "unknown singleCell.DEgenes.src";
46
+ }
47
+ }
48
+ export {
49
+ api,
50
+ validate_query_singleCell_DEgenes
51
+ };
@@ -1,13 +1,14 @@
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
+ import { validate_query_singleCell_DEgenes } from "./termdb.singlecellDEgenes.ts";
6
7
  import { gdc_validate_query_singleCell_samples, gdc_validate_query_singleCell_data } from "#src/mds3.gdc.js";
7
8
  const api = {
8
9
  endpoint: "termdb/singlecellSamples",
9
10
  methods: {
10
- get: {
11
+ all: {
11
12
  init,
12
13
  request: {
13
14
  typeId: "TermdbSinglecellsamplesRequest"
@@ -15,10 +16,6 @@ const api = {
15
16
  response: {
16
17
  typeId: "TermdbSinglecellsamplesResponse"
17
18
  }
18
- },
19
- post: {
20
- alternativeFor: "get",
21
- init
22
19
  }
23
20
  }
24
21
  };
@@ -73,6 +70,9 @@ async function validate_query_singleCell(ds, genome) {
73
70
  throw "unknown singleCell.geneExpression.src";
74
71
  }
75
72
  }
73
+ if (q.DEgenes) {
74
+ validate_query_singleCell_DEgenes(ds);
75
+ }
76
76
  }
77
77
  async function validateSamplesNative(S, ds) {
78
78
  const samples = {};
@@ -104,19 +104,25 @@ function validateDataNative(D, ds) {
104
104
  if (ds.queries.singleCell.geneExpression && q.gene) {
105
105
  geneExpMap = await ds.queries.singleCell.geneExpression.get({ sample: q.sample, gene: q.gene });
106
106
  }
107
+ const file2Lines = {};
107
108
  for (const plot of D.plots) {
109
+ if (!q.plots.includes(plot.name))
110
+ continue;
108
111
  const tsvfile = path.join(serverconfig.tpmasterdir, plot.folder, q.sample + plot.fileSuffix);
109
- try {
110
- await fs.promises.stat(tsvfile);
111
- } catch (e) {
112
- if (e.code == "ENOENT") {
113
- 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";
114
122
  }
115
- if (e.code == "EACCES")
116
- throw "cannot read file, permission denied";
117
- throw "failed to load sc data file";
123
+ file2Lines[tsvfile] = (await read_file(tsvfile)).trim().split("\n");
118
124
  }
119
- const lines = (await read_file(tsvfile)).trim().split("\n");
125
+ const lines = file2Lines[tsvfile];
120
126
  const cells = [];
121
127
  for (let i = 1; i < lines.length; i++) {
122
128
  const l = lines[i].split(" ");
@@ -148,47 +154,25 @@ function validateDataNative(D, ds) {
148
154
  };
149
155
  }
150
156
  function validateGeneExpressionNative(G) {
157
+ G.sample2gene2expressionBins = {};
151
158
  G.get = async (q) => {
152
- const tsvfile = path.join(serverconfig.tpmasterdir, G.folder, q.sample);
159
+ const rdsfile = path.join(serverconfig.tpmasterdir, G.folder, q.sample + ".rds");
160
+ try {
161
+ await fs.promises.stat(rdsfile);
162
+ } catch (e) {
163
+ return {};
164
+ }
165
+ let out;
153
166
  try {
154
- await fs.promises.stat(tsvfile);
167
+ out = JSON.parse(
168
+ await run_R(path.join(serverconfig.binpath, "utils", "getGeneFromMatrix.R"), null, [rdsfile, q.gene])
169
+ );
155
170
  } catch (e) {
156
- throw "geneExp matrix file not found or readable for this sample";
171
+ return {};
157
172
  }
158
- const header = (await get_header_txt(tsvfile)).split(" ");
159
- if (header.length == 0)
160
- throw "blank header line";
161
- return await grepMatrix4geneExpression(tsvfile, q.gene, header);
173
+ return out;
162
174
  };
163
175
  }
164
- function grepMatrix4geneExpression(tsvfile, gene, header) {
165
- return new Promise((resolve, reject) => {
166
- const cp = spawn("grep", ["-m", "1", gene + " ", tsvfile]);
167
- const out = [], err = [];
168
- cp.stdout.on("data", (d) => out.push(d));
169
- cp.stderr.on("data", (d) => err.push(d));
170
- cp.on("close", () => {
171
- const e = err.join("");
172
- if (e)
173
- reject(e);
174
- const cell2value = {};
175
- const line = out.join("").trim();
176
- if (!line) {
177
- resolve(cell2value);
178
- }
179
- const l = line.split(" ");
180
- if (l.length != header.length)
181
- reject(`number of fields differ between data line and header: ${l.length} ${header.length}`);
182
- for (let i = 1; i < l.length; i++) {
183
- const v = Number(l[i]);
184
- if (Number.isNaN(v))
185
- continue;
186
- cell2value[header[i]] = v;
187
- }
188
- resolve(cell2value);
189
- });
190
- });
191
- }
192
176
  export {
193
177
  api,
194
178
  validate_query_singleCell
@@ -39,7 +39,9 @@ function init({ genomes }) {
39
39
  };
40
40
  }
41
41
  async function trigger_gettermsbyid(q, res, tdb) {
42
- const terms = {};
42
+ const terms = {
43
+ terms: {}
44
+ };
43
45
  for (const id of q.ids) {
44
46
  const term = tdb.q.termjsonByOneid(id);
45
47
  if (term) {
@@ -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) {