@sjcrh/proteinpaint-server 2.40.7 → 2.41.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.40.7",
3
+ "version": "2.41.0",
4
4
  "description": "a genomics visualization tool for exploring a cohort's genotype and phenotype data",
5
5
  "main": "server.js",
6
6
  "bin": "start.js",
@@ -104,9 +104,7 @@ async function doClustering(data: any, q: TermdbClusterRequest) {
104
104
  const Rinputfile = path.join(serverconfig.cachedir, Math.random().toString() + '.json')
105
105
  await utils.write_file(Rinputfile, JSON.stringify(inputData))
106
106
  const Routput = JSON.parse(await lines2R(path.join(serverconfig.binpath, 'utils/hclust.R'), [], [Rinputfile]))
107
- fs.unlink(Rinputfile, (arg: any) => {
108
- return
109
- })
107
+ await fs.promises.unlink(Rinputfile)
110
108
 
111
109
  const row_names_index: number[] = Routput.RowOrder.map(row => inputData.row_names.indexOf(row.name)) // sorted rows. value is array index in input data
112
110
  const col_names_index: number[] = Routput.ColOrder.map(col => inputData.col_names.indexOf(col.name)) // sorted columns, value is array index from input array
@@ -165,6 +163,7 @@ export async function validate_query_geneExpression(ds: any, genome: any) {
165
163
 
166
164
  async function validateNative(q: GeneExpressionQueryNative, ds: any, genome: any) {
167
165
  q.file = path.join(serverconfig.tpmasterdir, q.file)
166
+ if (!q.samples) q.samples = []
168
167
  await utils.validate_tabixfile(q.file)
169
168
  q.nochr = await utils.tabix_is_nochr(q.file, null, genome)
170
169
  q.samples = [] as number[]
@@ -183,16 +182,6 @@ async function validateNative(q: GeneExpressionQueryNative, ds: any, genome: any
183
182
  console.log(q.samples.length, 'samples from geneExpression of', ds.label)
184
183
  }
185
184
 
186
- /*
187
- query exp data one gene at a time
188
- param{}
189
- .genes[{}]
190
- .gene=str
191
- .chr=str
192
- .start=int
193
- .stop=int
194
- .filterObj{}
195
- */
196
185
  q.get = async (param: TermdbClusterRequest) => {
197
186
  const limitSamples = await mayLimitSamples(param, q.samples, ds)
198
187
  if (limitSamples?.size == 0) {
@@ -203,17 +192,19 @@ async function validateNative(q: GeneExpressionQueryNative, ds: any, genome: any
203
192
  // has at least 1 sample passing filter and with exp data
204
193
  // TODO what if there's just 1 sample not enough for clustering?
205
194
  const bySampleId = {}
195
+ const samples = q.samples || []
206
196
  if (limitSamples) {
207
197
  for (const sid of limitSamples) {
208
198
  bySampleId[sid] = { label: ds.cohort.termdb.q.id2sampleName(sid) }
209
199
  }
210
200
  } else {
211
201
  // use all samples with exp data
212
- for (const sid of q.samples) {
202
+ for (const sid of samples) {
213
203
  bySampleId[sid] = { label: ds.cohort.termdb.q.id2sampleName(sid) }
214
204
  }
215
205
  }
216
206
 
207
+ // only valid genes with data are added. invalid genes or genes missing from data file is not added. backend returned genes is allowed to be fewer than supplied by client
217
208
  const gene2sample2value = new Map() // k: gene symbol, v: { sampleId : value }
218
209
 
219
210
  for (const g of param.genes) {
@@ -230,7 +221,7 @@ async function validateNative(q: GeneExpressionQueryNative, ds: any, genome: any
230
221
  g.chr = j.chr
231
222
  }
232
223
 
233
- gene2sample2value.set(g.gene, {})
224
+ const s2v = {}
234
225
  await utils.get_lines_bigfile({
235
226
  args: [q.file, (q.nochr ? g.chr?.replace('chr', '') : g.chr) + ':' + g.start + '-' + g.stop], // must do g.chr?.replace to avoid tsc error
236
227
  callback: line => {
@@ -238,16 +229,17 @@ async function validateNative(q: GeneExpressionQueryNative, ds: any, genome: any
238
229
  // case-insensitive match! FIXME if g.gene is alias won't work
239
230
  if (l[3].toLowerCase() != g.gene.toLowerCase()) return
240
231
  for (let i = 4; i < l.length; i++) {
241
- const sampleId = q.samples[i - 4]
232
+ const sampleId = samples[i - 4]
242
233
  if (limitSamples && !limitSamples.has(sampleId)) continue // doing filtering and sample of current column is not used
243
- // if l[i] is blank string?
234
+ if (!l[i]) continue // blank string
244
235
  const v = Number(l[i])
245
236
  if (Number.isNaN(v)) throw 'exp value not number'
246
- gene2sample2value.get(g.gene)[sampleId] = v
237
+ s2v[sampleId] = v
247
238
  }
248
239
  }
249
240
  } as any)
250
241
  // Above!! add "as any" to suppress a npx tsc alert
242
+ if (Object.keys(s2v).length) gene2sample2value.set(g.gene, s2v) // only add gene if has data
251
243
  }
252
244
  // pass blank byTermId to match with expected output structure
253
245
  const byTermId = {}
@@ -1,11 +1,11 @@
1
1
  import { getdescrstatsRequest, getdescrstatsResponse } from '#shared/types/routes/termdb.getdescrstats.ts'
2
- import * as termdbsql from '../src/termdb.sql.js'
2
+ import { get_rows_by_one_key } from '../src/termdb.sql.js'
3
3
  import Summarystats from '../shared/descriptive.stats.js'
4
4
 
5
5
  export const api: any = {
6
6
  endpoint: 'termdb/descrstats',
7
7
  methods: {
8
- get: {
8
+ all: {
9
9
  init,
10
10
  request: {
11
11
  typeId: 'getdescrstatsRequest'
@@ -20,7 +20,6 @@ export const api: any = {
20
20
  genome: 'hg38-test',
21
21
  dslabel: 'TermdbTest',
22
22
  embedder: 'localhost',
23
- getdescrstats: 1,
24
23
  tid: 'hrtavg',
25
24
  filter: {
26
25
  type: 'tvslst',
@@ -51,10 +50,6 @@ export const api: any = {
51
50
  }
52
51
  }
53
52
  ]
54
- },
55
- post: {
56
- alternativeFor: 'get',
57
- init
58
53
  }
59
54
  }
60
55
  }
@@ -84,14 +79,14 @@ async function trigger_getdescrstats(q: any, res: any, ds: any) {
84
79
  const term = ds.cohort.termdb.q.termjsonByOneid(q.tid)
85
80
  if (!term) throw 'invalid termid'
86
81
  if (term.type != 'float' && term.type != 'integer') throw 'not numerical term'
87
- const rows = await termdbsql.get_rows_by_one_key({
82
+ const rows = await get_rows_by_one_key({
88
83
  ds,
89
84
  key: q.tid,
90
- filter: q.filter ? (typeof q.filter == 'string' ? JSON.parse(q.filter) : q.filter) : null
85
+ filter: q.filter
91
86
  })
92
87
  const values: number[] = []
93
88
  for (const { value } of rows) {
94
- if (term.values && term.values[value] && term.values[value].uncomputable) {
89
+ if (term.values?.[value]?.uncomputable) {
95
90
  // skip uncomputable values
96
91
  continue
97
92
  }
@@ -152,15 +152,16 @@ function validateDataNative(D: SingleCellDataNative, ds: any) {
152
152
  const cellId = l[0],
153
153
  x = Number(l[4]), // FIXME standardize, or define idx in plot
154
154
  y = Number(l[5])
155
+ const category = l[plot.colorColumn?.index] || ''
155
156
  if (!cellId) throw 'cell id missing'
156
157
  if (!Number.isFinite(x) || !Number.isFinite(y)) throw 'x/y not number'
157
- cells.push({ cellId, x, y })
158
+ cells.push({ cellId, x, y, category })
158
159
 
159
160
  for (const tid of D.termIds) {
160
161
  tid2cellvalue[tid][cellId] = l[1]
161
162
  }
162
163
  }
163
- plots.push({ name: plot.name, cells })
164
+ plots.push({ name: plot.name, cells, colorBy: plot.colorColumn?.name })
164
165
  }
165
166
  if (plots.length == 0) {
166
167
  // no data available for this sample
@@ -12,7 +12,7 @@ import { get_samples } from '#src/termdb.sql.js'
12
12
  export const api = {
13
13
  endpoint: 'termdb/topVariablyExpressedGenes',
14
14
  methods: {
15
- get: {
15
+ all: {
16
16
  init,
17
17
  request: {
18
18
  typeId: 'TermdbTopVariablyExpressedGenesRequest'