@sjcrh/proteinpaint-server 2.30.3 → 2.30.5

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.
Files changed (51) hide show
  1. package/genome/hg19.js +3 -3
  2. package/genome/hg38.js +3 -3
  3. package/genome/hg38.test.js +3 -3
  4. package/package.json +1 -1
  5. package/routes/burden.ts +5 -2
  6. package/routes/gdc.maf.ts +39 -26
  7. package/routes/gdc.mafBuild.ts +5 -5
  8. package/routes/hicdata.ts +113 -0
  9. package/routes/hicstat.ts +55 -0
  10. package/routes/termdb.categories.ts +30 -6
  11. package/routes/termdb.getpercentile.ts +117 -0
  12. package/routes/termdb.termbyid.ts +5 -1
  13. package/server.js +1 -1
  14. package/src/serverconfig.js +11 -19
  15. package/cards/2dmaf.json +0 -37
  16. package/cards/README.md +0 -15
  17. package/cards/ai.json +0 -37
  18. package/cards/arc.json +0 -34
  19. package/cards/ase.json +0 -99
  20. package/cards/bam.json +0 -264
  21. package/cards/bampile.json +0 -22
  22. package/cards/bedj.json +0 -304
  23. package/cards/bigwig.json +0 -70
  24. package/cards/citations.json +0 -39
  25. package/cards/civicBtn.json +0 -15
  26. package/cards/databrowser.json +0 -276
  27. package/cards/disco.json +0 -69
  28. package/cards/dnanexusTips.txt +0 -221
  29. package/cards/exprank.json +0 -64
  30. package/cards/featuredDatasets.json +0 -84
  31. package/cards/fusioneditor.json +0 -34
  32. package/cards/gdcbam.json +0 -23
  33. package/cards/genefusion.json +0 -57
  34. package/cards/genomepaint.json +0 -122
  35. package/cards/hic.json +0 -79
  36. package/cards/index.json +0 -327
  37. package/cards/junction.json +0 -98
  38. package/cards/lollipop.json +0 -327
  39. package/cards/maf.timeline.json +0 -19
  40. package/cards/mavb.json +0 -47
  41. package/cards/nciGdcBtn.json +0 -21
  42. package/cards/pcmBtn.json +0 -16
  43. package/cards/ped2Btn.json +0 -16
  44. package/cards/pgv.json +0 -59
  45. package/cards/scatterplot.json +0 -31
  46. package/cards/singlecell.json +0 -25
  47. package/cards/study.json +0 -34
  48. package/cards/survivorBtn.json +0 -18
  49. package/cards/svview.txt +0 -101
  50. package/cards/tkFeatures.json +0 -92
  51. package/cards/tklist.json +0 -27
package/genome/hg19.js CHANGED
@@ -44,9 +44,9 @@ exports.default = {
44
44
  vpad: 4,
45
45
  categories: {
46
46
  coding: { color: '#004D99', label: 'Coding gene' },
47
- nonCoding: { color: '#009933', label: 'Noncoding gene' },
48
- problem: { color: '#FF3300', label: 'Problem' },
49
- pseudo: { color: '#FF00CC', label: 'Pseudogene' }
47
+ nonCoding: { color: '#008833', label: 'Noncoding gene' },
48
+ problem: { color: '#CC3300', label: 'Problem' },
49
+ pseudo: { color: '#CC00CC', label: 'Pseudogene' }
50
50
  }
51
51
  },
52
52
  {
package/genome/hg38.js CHANGED
@@ -48,9 +48,9 @@ exports.default = {
48
48
  translatecoding: true,
49
49
  categories: {
50
50
  coding: { color: '#004D99', label: 'Coding gene' },
51
- nonCoding: { color: '#009933', label: 'Noncoding gene' },
52
- problem: { color: '#FF3300', label: 'Problem' },
53
- pseudo: { color: '#FF00CC', label: 'Pseudogene' }
51
+ nonCoding: { color: '#008833', label: 'Noncoding gene' },
52
+ problem: { color: '#CC3300', label: 'Problem' },
53
+ pseudo: { color: '#CC00CC', label: 'Pseudogene' }
54
54
  },
55
55
  type: 'bedj',
56
56
  stackheight: 16,
@@ -24,9 +24,9 @@ var genome = {
24
24
  translatecoding: true,
25
25
  categories: {
26
26
  coding: { color: '#004D99', label: 'Coding gene' },
27
- nonCoding: { color: '#009933', label: 'Noncoding gene' },
28
- problem: { color: '#FF3300', label: 'Problem' },
29
- pseudo: { color: '#FF00CC', label: 'Pseudogene' }
27
+ nonCoding: { color: '#008833', label: 'Noncoding gene' },
28
+ problem: { color: '#CC3300', label: 'Problem' },
29
+ pseudo: { color: '#CC00CC', label: 'Pseudogene' }
30
30
  },
31
31
  type: 'bedj',
32
32
  name: 'GENCODE v41',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.30.3",
3
+ "version": "2.30.5",
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/burden.ts CHANGED
@@ -69,7 +69,10 @@ export const api = {
69
69
  }
70
70
  }
71
71
 
72
- async function getBurdenEstimates(q, ds) {
72
+ async function getBurdenEstimates(
73
+ q: { query: { [x: string]: any } },
74
+ ds: { cohort: { cumburden: { files: { fit: any; surv: any; sample: any } } } }
75
+ ) {
73
76
  const infile = path.join(serverconfig.cachedir, Math.random().toString() + '.json')
74
77
  for (const k in q.query) {
75
78
  q.query[k] = Number(q.query[k])
@@ -91,7 +94,7 @@ async function getBurdenEstimates(q, ds) {
91
94
  return estimates
92
95
  }
93
96
 
94
- function formatPayload(estimates) {
97
+ function formatPayload(estimates: object[]) {
95
98
  const rawKeys = Object.keys(estimates[0])
96
99
  const outKeys = [] as string[]
97
100
  const keys = [] as string[]
package/routes/gdc.maf.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GdcMafResponse, File } from '#shared/types/routes/gdc.maf.ts'
1
+ import { GdcMafRequest, GdcMafResponse, File } from '#shared/types/routes/gdc.maf.ts'
2
2
  import path from 'path'
3
3
  import got from 'got'
4
4
  import serverconfig from '#src/serverconfig.js'
@@ -6,36 +6,51 @@ import serverconfig from '#src/serverconfig.js'
6
6
  const apihost = process.env.PP_GDC_HOST || 'https://api.gdc.cancer.gov'
7
7
  const maxFileNumber = 1000
8
8
  const allowedWorkflowType = 'Aliquot Ensemble Somatic Variant Merging and Masking'
9
- const allowedExpStrategy = new Set(['WXS', 'Targeted Sequencing'])
10
9
  const maxTotalSizeCompressed = serverconfig.features.gdcMafMaxFileSize || 50000000 // 50Mb
11
10
 
12
11
  export const api = {
13
12
  endpoint: 'gdc/maf',
14
13
  methods: {
15
14
  get: {
16
- init({ genomes }) {
17
- return async (req: any, res: any): Promise<void> => {
18
- try {
19
- const g = genomes.hg38
20
- if (!g) throw 'hg38 missing'
21
- const ds = g.datasets.GDC
22
- if (!ds) throw 'hg38 GDC missing'
23
- const payload = await listMafFiles(req)
24
- res.send(payload)
25
- } catch (e: any) {
26
- res.send({ status: 'error', error: e.message || e })
27
- }
28
- }
29
- },
15
+ init,
30
16
  request: {
31
- typeId: null
32
- //valid: default to type checker
17
+ typeId: 'GdcMafRequest'
33
18
  },
34
19
  response: {
35
20
  typeId: 'GdcMafResponse'
36
21
  // will combine this with type checker
37
22
  //valid: (t) => {}
38
- }
23
+ },
24
+ examples: [
25
+ {
26
+ request: {
27
+ body: {
28
+ experimentalStrategy: 'WXS',
29
+ embedder: 'localhost'
30
+ }
31
+ },
32
+ response: {
33
+ header: { status: 200 }
34
+ }
35
+ }
36
+ ]
37
+ }
38
+ }
39
+ }
40
+
41
+ function init({ genomes }) {
42
+ return async (req: any, res: any): Promise<void> => {
43
+ try {
44
+ // g and ds are not used right now, but could be later
45
+ const g = genomes.hg38
46
+ if (!g) throw 'hg38 missing'
47
+ const ds = g.datasets.GDC
48
+ if (!ds) throw 'hg38 GDC missing'
49
+
50
+ const payload = await listMafFiles(req.query as GdcMafRequest)
51
+ res.send(payload)
52
+ } catch (e: any) {
53
+ res.send({ status: 'error', error: e.message || e })
39
54
  }
40
55
  }
41
56
  }
@@ -52,21 +67,19 @@ ds {
52
67
  }
53
68
  }
54
69
  */
55
- async function listMafFiles(req: any) {
56
- if (!allowedExpStrategy.has(req.query.experimentalStrategy)) throw 'invalid req.query.experimentalStrategy'
57
-
70
+ async function listMafFiles(q: GdcMafRequest) {
58
71
  const filters = {
59
72
  op: 'and',
60
73
  content: [
61
74
  { op: '=', content: { field: 'data_format', value: 'MAF' } },
62
- { op: '=', content: { field: 'experimental_strategy', value: req.query.experimentalStrategy } },
75
+ { op: '=', content: { field: 'experimental_strategy', value: q.experimentalStrategy } },
63
76
  { op: '=', content: { field: 'analysis.workflow_type', value: allowedWorkflowType } },
64
77
  { op: '=', content: { field: 'access', value: 'open' } } // delete if later to support controlled files
65
78
  ]
66
79
  }
67
80
 
68
- if (req.query.filter0) {
69
- filters.content.push(req.query.filter0)
81
+ if (q.filter0) {
82
+ filters.content.push(q.filter0)
70
83
  }
71
84
 
72
85
  const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }
@@ -152,7 +165,7 @@ async function listMafFiles(req: any) {
152
165
 
153
166
  file.case_submitter_id = c.submitter_id
154
167
  if (c.samples) {
155
- file.sample_types = c.samples.map(i => i.sample_type).sort()
168
+ file.sample_types = c.samples.map((i: { sample_type: any }) => i.sample_type).sort()
156
169
  // sort to show sample type names in consistent alphabetical order
157
170
  // otherwise one file shows 'Blood, Primary' and another shows 'Primary, Blood'
158
171
  // FIXME this includes samples not associated with current maf file
@@ -15,7 +15,7 @@ export const api = {
15
15
  return async (req: any, res: any): Promise<void> => {
16
16
  try {
17
17
  await buildMaf(req, res)
18
- } catch (e) {
18
+ } catch (e: any) {
19
19
  if (e.stack) console.log(e.stack)
20
20
  res.send({ status: 'error', error: e.message || e })
21
21
  }
@@ -42,10 +42,10 @@ req.query {
42
42
  res{}
43
43
  */
44
44
  async function buildMaf(req: any, res: any) {
45
- const t0 = new Date()
45
+ const t0 = Date.now()
46
46
 
47
47
  const fileLst2 = (await getFileLstUnderSizeLimit(req.query.fileIdLst)) as string[]
48
- console.log('test gdc maf sizes', new Date() - t0)
48
+ console.log('test gdc maf sizes', Date.now() - t0)
49
49
 
50
50
  const outFile = path.join(serverconfig.cachedir, 'gdcMaf.' + Math.random().toString()) // should be a gzipped file. does it need to end with '.gz' or it's auto-added?
51
51
 
@@ -57,7 +57,7 @@ async function buildMaf(req: any, res: any) {
57
57
 
58
58
  await run_rust('gdcmaf', JSON.stringify(arg))
59
59
 
60
- console.log('rust gdcmaf', new Date() - t0)
60
+ console.log('rust gdcmaf', Date.now() - t0)
61
61
 
62
62
  const data = await fs.promises.readFile(outFile)
63
63
 
@@ -68,7 +68,7 @@ async function buildMaf(req: any, res: any) {
68
68
  'Content-Disposition': 'attachment; filename=cohort.maf.gz',
69
69
  'Content-Length': data.length
70
70
  })
71
- res.end(Buffer.from(data, 'binary'))
71
+ res.end(Buffer.from(data as any, 'binary'))
72
72
  }
73
73
 
74
74
  /*
@@ -0,0 +1,113 @@
1
+ import { HicdataRequest, HicdataResponse, Item } from '#shared/types/routes/hicdata.ts'
2
+ import { fileurl } from '#src/utils.js'
3
+ import { spawn } from 'child_process'
4
+ import readline from 'readline'
5
+ import serverconfig from '#src/serverconfig.js'
6
+
7
+ export const api: any = {
8
+ endpoint: 'hicdata',
9
+ methods: {
10
+ get: {
11
+ init,
12
+ request: {
13
+ typeId: 'HicdataRequest'
14
+ },
15
+ response: {
16
+ typeId: 'HicdataResponse'
17
+ }
18
+ /*
19
+ examples: [
20
+ {
21
+ request: {
22
+ body: {
23
+ genome: 'hg38-test',
24
+ dslabel: 'TermdbTest',
25
+ embedder: 'localhost',
26
+ gettermbyid: 'subcohort'
27
+ }
28
+ },
29
+ response: {
30
+ header: { status: 200 }
31
+ }
32
+ }
33
+ ]
34
+ */
35
+ },
36
+ post: {
37
+ alternativeFor: 'get',
38
+ init
39
+ }
40
+ }
41
+ }
42
+
43
+ function init({ genomes }) {
44
+ return async (req: any, res: any): Promise<void> => {
45
+ try {
46
+ const payload = await handle_hicdata(req.query as HicdataRequest)
47
+ res.send(payload)
48
+ } catch (e) {
49
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
50
+ // @ts-ignore
51
+ res.send({ error: e?.message || e })
52
+ if (e instanceof Error && e.stack) console.log(e)
53
+ }
54
+ }
55
+ }
56
+
57
+ function handle_hicdata(q: HicdataRequest) {
58
+ return new Promise((resolve, reject) => {
59
+ const [e, file, isurl] = fileurl({ query: q })
60
+ if (e) reject({ error: 'illegal file name' })
61
+
62
+ const par = [
63
+ // TODO add option for observed/oe
64
+ q.nmeth || 'NONE',
65
+ file,
66
+ q.pos1,
67
+ q.pos2,
68
+ q.isfrag ? 'FRAG' : 'BP',
69
+ q.resolution
70
+ ]
71
+
72
+ const ps = spawn(serverconfig.hicstraw, par)
73
+ const rl = readline.createInterface({ input: ps.stdout })
74
+
75
+ const items = [] as Item[]
76
+ const erroutput = [] as string[]
77
+ let linenot3fields = 0
78
+ let fieldnotnumerical = 0
79
+
80
+ rl.on('line', line => {
81
+ // straw output: pos1 \t pos2 \t value
82
+ const l = line.split('\t')
83
+ if (l.length != 3) {
84
+ linenot3fields++
85
+ return
86
+ }
87
+ const n1 = Number.parseInt(l[0])
88
+ const n2 = Number.parseInt(l[1])
89
+ const v = Number.parseFloat(l[2])
90
+ if (Number.isNaN(n1) || Number.isNaN(n2) || Number.isNaN(v)) {
91
+ fieldnotnumerical++
92
+ return
93
+ }
94
+ if (q.mincutoff != undefined && v <= q.mincutoff) {
95
+ return
96
+ }
97
+ items.push([n1, n2, v] as Item)
98
+ })
99
+
100
+ ps.stderr.on('data', i => erroutput.push(i))
101
+ ps.on('close', () => {
102
+ const err = erroutput.join('')
103
+ if (err) reject({ error: err })
104
+
105
+ if (linenot3fields) reject({ error: linenot3fields + ' lines have other than 3 fields' })
106
+
107
+ if (fieldnotnumerical)
108
+ reject({ error: fieldnotnumerical + ' lines have non-numerical values in any of the 3 fields' })
109
+
110
+ resolve({ items })
111
+ })
112
+ })
113
+ }
@@ -0,0 +1,55 @@
1
+ import { fileurl, file_is_readable } from '#src/utils.js'
2
+ import { do_hicstat } from '#src/hicstat.ts'
3
+ import { HicstatRequestWithValidation } from '#shared/types/routes/hicstat.ts'
4
+
5
+ export const api = {
6
+ endpoint: 'hicstat',
7
+ methods: {
8
+ get: {
9
+ init,
10
+ request: {
11
+ typeId: 'HicstatRequest'
12
+ },
13
+ response: {
14
+ typeId: 'HicstatResponse'
15
+ },
16
+ examples: [
17
+ {
18
+ request: {
19
+ body: {
20
+ genome: 'hg19',
21
+ file: 'proteinpaint_demo/hg19/hic/hic_demo.hic',
22
+ embedder: 'localhost'
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() {
39
+ return async (req: HicstatRequestWithValidation, res: any): Promise<void> => {
40
+ try {
41
+ const [e, file, isurl] = fileurl(req)
42
+ if (e) throw 'illegal file name'
43
+ if (!isurl) {
44
+ await file_is_readable(file)
45
+ }
46
+ const out = await do_hicstat(file, isurl)
47
+ res.send({ out })
48
+ } catch (e: any) {
49
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
50
+ // @ts-ignore
51
+ res.send({ error: e?.message || e })
52
+ if (e instanceof Error && e.stack) console.log(e)
53
+ }
54
+ }
55
+ }
@@ -87,7 +87,13 @@ function init({ genomes }) {
87
87
  }
88
88
  }
89
89
 
90
- async function trigger_getcategories(q, res, tdb, ds, genome) {
90
+ async function trigger_getcategories(
91
+ q: { tid: string | number; type: string; filter: any; term1_q: any; currentGeneNames: any },
92
+ res: { send: (arg0: { lst: any[]; orderedLabels: any }) => void },
93
+ tdb: { q: { termjsonByOneid: (arg0: any) => any } },
94
+ ds: { assayAvailability: { byDt: { [s: string]: any } | ArrayLike<any> } },
95
+ genome: any
96
+ ) {
91
97
  // thin wrapper of get_summary
92
98
  // works for all types of terms
93
99
  if (!q.tid) throw '.tid missing'
@@ -105,9 +111,9 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
105
111
  const data = await getData(arg, ds, genome)
106
112
  if (data.error) throw data.error
107
113
 
108
- const lst = []
114
+ const lst = [] as any[]
109
115
  if (q.type == 'geneVariant') {
110
- const samples = data.samples
116
+ const samples = data.samples as { [sampleId: string]: any }
111
117
  const dtClassMap = new Map()
112
118
  if (ds.assayAvailability?.byDt) {
113
119
  for (const [dtType, dtValue] of Object.entries(ds.assayAvailability.byDt)) {
@@ -116,7 +122,7 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
116
122
  }
117
123
  }
118
124
  }
119
- const sampleCountedFor = new Set() // if the sample is conunted for the
125
+ const sampleCountedFor = new Set() // if the sample is counted
120
126
  for (const [sampleId, sampleData] of Object.entries(samples)) {
121
127
  const values = sampleData[q.tid].values
122
128
  sampleCountedFor.clear()
@@ -175,7 +181,9 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
175
181
  samplecount: count,
176
182
  key,
177
183
  label:
178
- data.refs?.byTermId?.[q.tid]?.events?.find(e => e.event === key).label || term?.values?.[key]?.label || key
184
+ data.refs?.byTermId?.[q.tid]?.events?.find((e: { event: any }) => e.event === key).label ||
185
+ term?.values?.[key]?.label ||
186
+ key
179
187
  })
180
188
  }
181
189
  }
@@ -195,7 +203,23 @@ async function trigger_getcategories(q, res, tdb, ds, genome) {
195
203
  })
196
204
  }
197
205
 
198
- function getDefaultQ(term, q) {
206
+ function getDefaultQ(
207
+ term: { type: string; bins: { default: any } },
208
+ q: {
209
+ mode?: any
210
+ breaks?: any
211
+ bar_by_grade?: any
212
+ bar_by_children?: any
213
+ value_by_max_grade?: any
214
+ value_by_most_recent?: any
215
+ value_by_computable_grade?: any
216
+ tid?: string | number
217
+ type?: string
218
+ filter?: any
219
+ term1_q?: any
220
+ currentGeneNames?: any
221
+ }
222
+ ) {
199
223
  if (term.type == 'categorical') return {}
200
224
  if (term.type == 'survival') return {}
201
225
  if (term.type == 'integer' || term.type == 'float') return term.bins.default
@@ -0,0 +1,117 @@
1
+ // import { getpercentileRequest, getpercentileResponse } from '#shared/types/routes/termdb.getpercentile'
2
+ import * as termdbsql from '#src/termdb.sql.js'
3
+ import computePercentile from '#shared/compute.percentile.js'
4
+
5
+ export const api: any = {
6
+ endpoint: 'termdb/getpercentile',
7
+ methods: {
8
+ get: {
9
+ init,
10
+ request: {
11
+ typeId: 'getpercentileRequest'
12
+ },
13
+ response: {
14
+ typeId: 'getpercentileResponse'
15
+ },
16
+ examples: [
17
+ {
18
+ request: {
19
+ body: {
20
+ genome: 'hg38-test',
21
+ dslabel: 'TermdbTest',
22
+ embedder: 'localhost',
23
+ getpercentile: [50],
24
+ tid: 'agedx',
25
+ filter: {
26
+ type: 'tvslst',
27
+ in: true,
28
+ join: '',
29
+ lst: [
30
+ {
31
+ tag: 'cohortFilter',
32
+ type: 'tvs',
33
+ tvs: {
34
+ term: {
35
+ name: 'Cohort',
36
+ type: 'categorical',
37
+ values: { ABC: { label: 'ABC' }, XYZ: { label: 'XYZ' } },
38
+ id: 'subcohort',
39
+ isleaf: false,
40
+ groupsetting: { disabled: true }
41
+ },
42
+ values: [{ key: 'ABC', label: 'ABC' }]
43
+ }
44
+ }
45
+ ]
46
+ }
47
+ }
48
+ },
49
+ response: {
50
+ header: { status: 200 }
51
+ }
52
+ }
53
+ ]
54
+ },
55
+ post: {
56
+ alternativeFor: 'get',
57
+ init
58
+ }
59
+ }
60
+ }
61
+
62
+ function init({ genomes }) {
63
+ return async (req: any, res: any): Promise<void> => {
64
+ const q = req.query // as getpercentileRequest
65
+ try {
66
+ const g = genomes[req.query.genome]
67
+ if (!g) throw 'invalid genome name'
68
+ const ds = g.datasets[req.query.dslabel]
69
+ if (!ds) throw 'invalid dataset name'
70
+ await trigger_getpercentile(q, res, ds) // as getpercentileResponse
71
+ } catch (e) {
72
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
73
+ // @ts-ignore
74
+ res.send({ error: e?.message || e })
75
+ if (e instanceof Error && e.stack) console.log(e)
76
+ }
77
+ }
78
+ }
79
+
80
+ async function trigger_getpercentile(
81
+ q: { tid: string; getpercentile: number[]; filter: string },
82
+ res: { send: (arg0: { values: number[] }) => void },
83
+ ds: { cohort: { termdb: { q: { termjsonByOneid: (arg0: any) => any } } } }
84
+ ) {
85
+ const term = ds.cohort.termdb.q.termjsonByOneid(q.tid)
86
+ if (!term) throw 'invalid termid'
87
+ if (term.type != 'float' && term.type != 'integer') throw 'not numerical term'
88
+ const percentile_lst = q.getpercentile
89
+ const perc_values = [] as number[]
90
+ const values = [] as number[]
91
+ const rows = await termdbsql.get_rows_by_one_key({
92
+ ds,
93
+ key: q.tid,
94
+ filter: q.filter ? (typeof q.filter == 'string' ? JSON.parse(q.filter) : q.filter) : null
95
+ })
96
+ for (const { value } of rows) {
97
+ if (term.values && term.values[value] && term.values[value].uncomputable) {
98
+ // skip uncomputable values
99
+ continue
100
+ }
101
+
102
+ if (term.skip0forPercentile && value == 0) {
103
+ // quick fix: when the flag is true, will exclude 0 values from percentile computing
104
+ // to address an issue with computing knots
105
+ continue
106
+ }
107
+
108
+ values.push(Number(value))
109
+ }
110
+
111
+ // compute percentiles
112
+ for (const percentile of percentile_lst) {
113
+ const perc_value = computePercentile(values, percentile)
114
+ perc_values.push(perc_value)
115
+ }
116
+ res.send({ values: perc_values })
117
+ }
@@ -56,7 +56,11 @@ function init({ genomes }) {
56
56
  }
57
57
  }
58
58
 
59
- async function trigger_gettermbyid(q, res, tdb) {
59
+ async function trigger_gettermbyid(
60
+ q: { gettermbyid: any },
61
+ res: { send: (arg0: { term: any }) => void },
62
+ tdb: { q: { termjsonByOneid: (arg0: any) => any } }
63
+ ) {
60
64
  const t = tdb.q.termjsonByOneid(q.gettermbyid)
61
65
  res.send({
62
66
  term: t ? copy_term(t) : undefined