@sjcrh/proteinpaint-server 2.39.0 → 2.39.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.39.0",
3
+ "version": "2.39.1",
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",
package/routes/gdc.maf.ts CHANGED
@@ -3,15 +3,28 @@ import path from 'path'
3
3
  import got from 'got'
4
4
  import serverconfig from '#src/serverconfig.js'
5
5
 
6
- const apihost = process.env.PP_GDC_HOST || 'https://api.gdc.cancer.gov'
7
- const maxFileNumber = 1000
6
+ /*
7
+ this route lists available gdc MAF files based on user's cohort filter
8
+ and return them to client to be shown in a table for selection
9
+ */
10
+
11
+ const apihost = process.env.PP_GDC_HOST || 'https://api.gdc.cancer.gov' // to switch to serverconfig export
12
+
13
+ const maxFileNumber = 1000 // determines max number of files to return to client
14
+ // preliminary testing:
15
+ // 36s for 1000 (87Mb)
16
+ // 78s for 2000 (177Mb)
17
+ // if safe to increase to 2000, maybe fast when this runs in gdc env
18
+
8
19
  const allowedWorkflowType = 'Aliquot Ensemble Somatic Variant Merging and Masking'
9
- const maxTotalSizeCompressed = serverconfig.features.gdcMafMaxFileSize || 50000000 // 50Mb
20
+
21
+ // change to 400 so it won't limit number of files; should keep this setting as a safeguard; also it's fast to check file size (.5s in gdc.mafBuild.ts)
22
+ export const maxTotalSizeCompressed = serverconfig.features.gdcMafMaxFileSize || 400000000 // 400Mb
10
23
 
11
24
  export const api = {
12
25
  endpoint: 'gdc/maf',
13
26
  methods: {
14
- get: {
27
+ all: {
15
28
  init,
16
29
  request: {
17
30
  typeId: 'GdcMafRequest'
@@ -5,9 +5,9 @@ import { run_rust_stream } from '@sjcrh/proteinpaint-rust'
5
5
  import serverconfig from '#src/serverconfig.js'
6
6
  import Readable from 'stream'
7
7
  import { GdcMafBuildRequest } from '#shared/types/routes/gdc.mafBuild.ts'
8
+ import { maxTotalSizeCompressed } from './gdc.maf.ts'
8
9
 
9
10
  const apihost = process.env.PP_GDC_HOST || 'https://api.gdc.cancer.gov'
10
- const maxTotalSizeCompressed = serverconfig.features.gdcMafMaxFileSize || 50000000 // 50Mb
11
11
 
12
12
  export const api = {
13
13
  endpoint: 'gdc/mafBuild',
@@ -43,6 +43,7 @@ async function buildMaf(q: GdcMafBuildRequest, res: any) {
43
43
  const t0 = Date.now()
44
44
 
45
45
  const fileLst2 = (await getFileLstUnderSizeLimit(q.fileIdLst)) as string[]
46
+
46
47
  if (serverconfig.debugmode)
47
48
  console.log(
48
49
  `${fileLst2.length} out of ${q.fileIdLst.length} input MAF files accepted by size limit`,
@@ -60,11 +61,12 @@ async function buildMaf(q: GdcMafBuildRequest, res: any) {
60
61
  res.setHeader('Content-Disposition', 'attachment; filename=cohort.maf.gz')
61
62
  rustStream.pipe(res)
62
63
 
63
- if (serverconfig.debugmode) console.log('rust gdcmaf', Date.now() - t0)
64
-
65
64
  rustStream.on('end', () => {
65
+ // report amount of time taken to run rust
66
+ if (serverconfig.debugmode) console.log('rust gdcmaf', Date.now() - t0)
66
67
  res.end()
67
68
  })
69
+
68
70
  rustStream.on('error', err => {
69
71
  console.error(err)
70
72
  res.statusCode = 500
@@ -85,7 +87,7 @@ async function getFileLstUnderSizeLimit(lst: string[]) {
85
87
  op: 'in',
86
88
  content: { field: 'file_id', value: lst }
87
89
  },
88
- size: 1000,
90
+ size: 10000,
89
91
  fields: 'file_size'
90
92
  }
91
93
  const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }
package/routes/hicdata.ts CHANGED
@@ -40,7 +40,7 @@ export const api: any = {
40
40
  }
41
41
  }
42
42
 
43
- function init({ genomes }) {
43
+ function init() {
44
44
  return async (req: any, res: any): Promise<void> => {
45
45
  try {
46
46
  const payload = await handle_hicdata(req.query as HicdataRequest)
@@ -60,7 +60,7 @@ function handle_hicdata(q: HicdataRequest) {
60
60
  if (e) reject({ error: 'illegal file name' })
61
61
 
62
62
  const par = [
63
- // TODO add option for observed/oe
63
+ q.matrixType || 'observed',
64
64
  q.nmeth || 'NONE',
65
65
  file,
66
66
  q.pos1,
@@ -1,5 +1,4 @@
1
1
  import { TermdbClusterRequest, TermdbClusterResponse } from '#shared/types/routes/termdb.cluster.ts'
2
- import fs from 'fs'
3
2
  import path from 'path'
4
3
  import * as utils from '#src/utils.js'
5
4
  import serverconfig from '#src/serverconfig.js'
@@ -12,7 +11,7 @@ import { dtgeneexpression } from '#shared/common.js'
12
11
  export const api = {
13
12
  endpoint: 'termdb/cluster',
14
13
  methods: {
15
- get: {
14
+ all: {
16
15
  init,
17
16
  request: {
18
17
  typeId: 'TermdbClusterRequest'
@@ -20,10 +19,6 @@ export const api = {
20
19
  response: {
21
20
  typeId: 'TermdbClusterResponse'
22
21
  }
23
- },
24
- post: {
25
- alternativeFor: 'get',
26
- init
27
22
  }
28
23
  }
29
24
  }
@@ -121,8 +116,8 @@ async function validateNative(q: GeneExpressionQueryNative, ds: any, genome: any
121
116
  q.get = async (param: TermdbClusterRequest) => {
122
117
  const limitSamples = await mayLimitSamples(param, q.samples, ds)
123
118
  if (limitSamples?.size == 0) {
124
- // got 0 sample after filtering, return blank array for no data
125
- return new Set()
119
+ // got 0 sample after filtering, must still return expected structure with no data
120
+ return { gene2sample2value: new Set(), byTermId: {}, bySampleId: {} }
126
121
  }
127
122
 
128
123
  // has at least 1 sample passing filter and with exp data
@@ -1,7 +1,6 @@
1
1
  import { getdescrstatsRequest, getdescrstatsResponse } from '#shared/types/routes/termdb.getdescrstats.ts'
2
- import roundValue from '#shared/roundValue.js'
3
- import computePercentile from '#shared/compute.percentile.js'
4
- import * as termdbsql from '#src/termdb.sql.js'
2
+ import * as termdbsql from '../src/termdb.sql.js'
3
+ import Summarystats from '../shared/descriptive.stats.js'
5
4
 
6
5
  export const api: any = {
7
6
  endpoint: 'termdb/descrstats',
@@ -104,42 +103,5 @@ async function trigger_getdescrstats(q: any, res: any, ds: any) {
104
103
  }
105
104
  values.push(value)
106
105
  }
107
-
108
- // compute statistics
109
- // total
110
- const total = values.length
111
-
112
- // mean
113
- const sum = values.reduce((a, b) => a + b, 0)
114
- const mean = sum / total
115
-
116
- // percentiles
117
- const p25 = computePercentile(values, 25)
118
- const median = computePercentile(values, 50)
119
- const p75 = computePercentile(values, 75)
120
-
121
- // standard deviation
122
- // get sum of squared differences from mean
123
- const sumSqDiff = values.map(v => (v - mean) ** 2).reduce((a, b) => a + b, 0)
124
- // get variance
125
- const variance = sumSqDiff / (values.length - 1)
126
- // get standard deviation
127
- const sd = Math.sqrt(variance)
128
-
129
- // min/max
130
- const min = Math.min(...values)
131
- const max = Math.max(...values)
132
-
133
- res.send({
134
- values: [
135
- { id: 'total', label: 'n', value: total },
136
- { id: 'min', label: 'Minimum', value: roundValue(min, 2) },
137
- { id: 'p25', label: '1st quartile', value: roundValue(p25, 2) },
138
- { id: 'median', label: 'Median', value: roundValue(median, 2) },
139
- { id: 'mean', label: 'Mean', value: roundValue(mean, 2) },
140
- { id: 'p75', label: '3rd quartile', value: roundValue(p75, 2) },
141
- { id: 'max', label: 'Maximum', value: roundValue(max, 2) },
142
- { id: 'sd', label: 'Standard deviation', value: roundValue(sd, 2) }
143
- ]
144
- } as getdescrstatsResponse)
106
+ res.send(Summarystats(values) as getdescrstatsResponse)
145
107
  }
@@ -0,0 +1,67 @@
1
+ import { getroottermRequest, getroottermResponse } from '#shared/types/routes/termdb.getrootterm.ts'
2
+
3
+ export const api: any = {
4
+ endpoint: 'termdb/rootterm',
5
+ methods: {
6
+ get: {
7
+ init,
8
+ request: {
9
+ typeId: 'getroottermRequest'
10
+ },
11
+ response: {
12
+ typeId: 'getroottermResponse'
13
+ },
14
+ examples: [
15
+ {
16
+ request: {
17
+ body: {
18
+ genome: 'hg38-test',
19
+ dslabel: 'TermdbTest',
20
+ embedder: 'localhost',
21
+ default_rootterm: 1,
22
+ cohortValues: 'ABC'
23
+ }
24
+ },
25
+ response: {
26
+ header: { status: 200 }
27
+ }
28
+ }
29
+ ]
30
+ },
31
+ post: {
32
+ alternativeFor: 'get',
33
+ init
34
+ }
35
+ }
36
+ }
37
+
38
+ function init({ genomes }) {
39
+ return async (req: any, res: any): Promise<void> => {
40
+ const q = req.query as getroottermRequest
41
+ try {
42
+ const g = genomes[req.query.genome]
43
+ if (!g) throw 'invalid genome name'
44
+ const ds = g.datasets[req.query.dslabel]
45
+ if (!ds) throw 'invalid dataset name'
46
+ const tdb = ds.cohort.termdb
47
+ if (!tdb) throw 'invalid termdb object'
48
+
49
+ await trigger_rootterm(q, res, tdb) // as getroottermResponse
50
+ } catch (e) {
51
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
52
+ // @ts-ignore
53
+ res.send({ error: e?.message || e })
54
+ if (e instanceof Error && e.stack) console.log(e)
55
+ }
56
+ }
57
+ }
58
+
59
+ async function trigger_rootterm(
60
+ q: { cohortValues: any; treeFilter: any },
61
+ res: { send: (arg0: { lst: any }) => void },
62
+ tdb: { q: { getRootTerms: (arg0: any, arg1: any) => any } }
63
+ ) {
64
+ const cohortValues = q.cohortValues ? q.cohortValues : ''
65
+ const treeFilter = q.treeFilter ? q.treeFilter : ''
66
+ res.send({ lst: await tdb.q.getRootTerms(cohortValues, treeFilter) } as getroottermResponse)
67
+ }
@@ -0,0 +1,82 @@
1
+ import { gettermchildrenRequest, gettermchildrenResponse } from '#shared/types/routes/termdb.gettermchildren.ts'
2
+ import { copy_term } from '#src/termdb.js'
3
+
4
+ export const api: any = {
5
+ endpoint: 'termdb/termchildren',
6
+ methods: {
7
+ get: {
8
+ init,
9
+ request: {
10
+ typeId: 'gettermchildrenRequest'
11
+ },
12
+ response: {
13
+ typeId: 'gettermchildrenResponse'
14
+ },
15
+ examples: [
16
+ {
17
+ request: {
18
+ body: {
19
+ genome: 'hg38-test',
20
+ dslabel: 'TermdbTest',
21
+ embedder: 'localhost',
22
+ get_children: 1,
23
+ cohortValues: 'ABC',
24
+ tid: 'GO:0000001'
25
+ }
26
+ },
27
+ response: {
28
+ header: { status: 200 }
29
+ }
30
+ }
31
+ ]
32
+ },
33
+ post: {
34
+ alternativeFor: 'get',
35
+ init
36
+ }
37
+ }
38
+ }
39
+
40
+ function init({ genomes }) {
41
+ return async (req: any, res: any): Promise<void> => {
42
+ const q = req.query as gettermchildrenRequest
43
+ try {
44
+ const g = genomes[req.query.genome]
45
+ if (!g) throw 'invalid genome name'
46
+ const ds = g.datasets[req.query.dslabel]
47
+ if (!ds) throw 'invalid dataset name'
48
+ const tdb = ds.cohort.termdb
49
+ if (!tdb) throw 'invalid termdb object'
50
+
51
+ await trigger_children(q, res, tdb)
52
+ } catch (e) {
53
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
54
+ // @ts-ignore
55
+ res.send({ error: e?.message || e })
56
+ if (e instanceof Error && e.stack) console.log(e)
57
+ }
58
+ }
59
+ }
60
+
61
+ async function trigger_children(
62
+ q: {
63
+ genome?: string
64
+ dslabel?: string
65
+ embedder?: string
66
+ get_children?: number
67
+ tid: any
68
+ cohortValues?: any
69
+ treeFilter?: any
70
+ },
71
+ res: { send: (arg0: gettermchildrenResponse) => void },
72
+ tdb: { q: { getTermChildren: (arg0: any, arg1: any, arg2: any) => any } }
73
+ ) {
74
+ /* get children terms
75
+ may apply ssid: a premade sample set
76
+ */
77
+ if (!q.tid) throw 'no parent term id'
78
+ const cohortValues = q.cohortValues ? q.cohortValues : ''
79
+ const treeFilter = q.treeFilter ? q.treeFilter : ''
80
+ const terms = await tdb.q.getTermChildren(q.tid, cohortValues, treeFilter)
81
+ res.send({ lst: terms.map(copy_term) } as gettermchildrenResponse)
82
+ }