@sjcrh/proteinpaint-server 2.30.4 → 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.
- package/genome/hg19.js +3 -3
- package/genome/hg38.js +3 -3
- package/genome/hg38.test.js +3 -3
- package/package.json +1 -1
- package/routes/burden.ts +5 -2
- package/routes/gdc.maf.ts +39 -26
- package/routes/gdc.mafBuild.ts +5 -5
- package/routes/hicdata.ts +113 -0
- package/routes/hicstat.ts +55 -0
- package/routes/termdb.categories.ts +30 -6
- package/routes/termdb.getpercentile.ts +117 -0
- package/routes/termdb.termbyid.ts +5 -1
- package/server.js +1 -1
- package/src/serverconfig.js +0 -19
- package/cards/2dmaf.json +0 -37
- package/cards/README.md +0 -15
- package/cards/ai.json +0 -37
- package/cards/arc.json +0 -34
- package/cards/ase.json +0 -99
- package/cards/bam.json +0 -264
- package/cards/bampile.json +0 -22
- package/cards/bedj.json +0 -304
- package/cards/bigwig.json +0 -70
- package/cards/citations.json +0 -39
- package/cards/civicBtn.json +0 -15
- package/cards/databrowser.json +0 -276
- package/cards/disco.json +0 -69
- package/cards/dnanexusTips.txt +0 -221
- package/cards/exprank.json +0 -64
- package/cards/featuredDatasets.json +0 -84
- package/cards/fusioneditor.json +0 -34
- package/cards/gdcbam.json +0 -23
- package/cards/genefusion.json +0 -57
- package/cards/genomepaint.json +0 -122
- package/cards/hic.json +0 -79
- package/cards/index.json +0 -327
- package/cards/junction.json +0 -98
- package/cards/lollipop.json +0 -327
- package/cards/maf.timeline.json +0 -19
- package/cards/mavb.json +0 -47
- package/cards/nciGdcBtn.json +0 -21
- package/cards/pcmBtn.json +0 -16
- package/cards/ped2Btn.json +0 -16
- package/cards/pgv.json +0 -59
- package/cards/scatterplot.json +0 -31
- package/cards/singlecell.json +0 -25
- package/cards/study.json +0 -34
- package/cards/survivorBtn.json +0 -18
- package/cards/svview.txt +0 -101
- package/cards/tkFeatures.json +0 -92
- 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: '#
|
|
48
|
-
problem: { color: '#
|
|
49
|
-
pseudo: { color: '#
|
|
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: '#
|
|
52
|
-
problem: { color: '#
|
|
53
|
-
pseudo: { color: '#
|
|
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,
|
package/genome/hg38.test.js
CHANGED
|
@@ -24,9 +24,9 @@ var genome = {
|
|
|
24
24
|
translatecoding: true,
|
|
25
25
|
categories: {
|
|
26
26
|
coding: { color: '#004D99', label: 'Coding gene' },
|
|
27
|
-
nonCoding: { color: '#
|
|
28
|
-
problem: { color: '#
|
|
29
|
-
pseudo: { color: '#
|
|
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
package/routes/burden.ts
CHANGED
|
@@ -69,7 +69,10 @@ export const api = {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
async function getBurdenEstimates(
|
|
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
|
|
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:
|
|
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(
|
|
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:
|
|
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 (
|
|
69
|
-
filters.content.push(
|
|
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
|
package/routes/gdc.mafBuild.ts
CHANGED
|
@@ -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 =
|
|
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',
|
|
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',
|
|
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(
|
|
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
|
|
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 ||
|
|
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(
|
|
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(
|
|
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
|