@sjcrh/proteinpaint-server 2.26.0 → 2.27.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/genome/hg19.js +0 -6
- package/genome/hg38.js +0 -6
- package/package.json +12 -10
- package/routes/README.md +84 -0
- package/routes/burden.ts +140 -0
- package/routes/gdcMaf.ts +106 -0
- package/routes/genelookup.ts +4 -12
- package/routes/healthcheck.ts +3 -46
- package/routes/termdb.violin.ts +78 -0
- package/server.js +1 -1
- package/utils/burden.R +334 -0
package/genome/hg19.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
var clinvar_1 = require("../dataset/clinvar");
|
|
4
3
|
exports.default = {
|
|
5
4
|
species: 'human',
|
|
6
5
|
genomefile: 'genomes/hg19.gz',
|
|
@@ -22,11 +21,6 @@ exports.default = {
|
|
|
22
21
|
db: 'utils/meme/motif_databases/HUMAN/HOCOMOCOv11_full_HUMAN_mono_meme_format.meme',
|
|
23
22
|
annotationfile: 'utils/meme/motif_databases/HUMAN/HOCOMOCOv11_full_annotation_HUMAN_mono.tsv'
|
|
24
23
|
},
|
|
25
|
-
clinvarVCF: {
|
|
26
|
-
file: 'hg19/clinvar.hg19.hgvs_short.vep.bcf.gz',
|
|
27
|
-
infokey: 'CLNSIG',
|
|
28
|
-
categories: clinvar_1.clinsig
|
|
29
|
-
},
|
|
30
24
|
tracks: [
|
|
31
25
|
{
|
|
32
26
|
__isgene: true,
|
package/genome/hg38.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
var clinvar_1 = require("../dataset/clinvar");
|
|
4
3
|
var cgc_js_1 = require("./cgc.js");
|
|
5
4
|
exports.default = {
|
|
6
5
|
species: 'human',
|
|
@@ -30,11 +29,6 @@ exports.default = {
|
|
|
30
29
|
db: 'utils/meme/motif_databases/HUMAN/HOCOMOCOv11_full_HUMAN_mono_meme_format.meme',
|
|
31
30
|
annotationfile: 'utils/meme/motif_databases/HUMAN/HOCOMOCOv11_full_annotation_HUMAN_mono.tsv'
|
|
32
31
|
},
|
|
33
|
-
clinvarVCF: {
|
|
34
|
-
file: 'hg38/clinvar.hg38.hgvs_short.vep.bcf.gz',
|
|
35
|
-
infokey: 'CLNSIG',
|
|
36
|
-
categories: clinvar_1.clinsig
|
|
37
|
-
},
|
|
38
32
|
tracks: [
|
|
39
33
|
{
|
|
40
34
|
__isgene: true,
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.27.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",
|
|
7
7
|
"imports": {
|
|
8
|
-
"#shared/*": "./shared
|
|
8
|
+
"#shared/*": "./shared/*",
|
|
9
|
+
"#shared/types/*": "./shared/types/*",
|
|
9
10
|
"#src/*": "./src/*",
|
|
10
11
|
"#routes/*": "./routes/*"
|
|
11
12
|
},
|
|
@@ -15,17 +16,18 @@
|
|
|
15
16
|
"start": "tsc --esModuleInterop genome/*.ts dataset/*.ts && nodemon --enable-source-maps server.js --watch ./server*.js* --watch dataset/*.ts --watch genome/*.ts",
|
|
16
17
|
"pretest": "tsc --esModuleInterop genome/*.ts dataset/*.ts && ./test/pretest.js",
|
|
17
18
|
"test": "webpack --env NODE_ENV=test exportsFilename=all-test-context.js && node --enable-source-maps test/serverTests.js",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
19
|
+
"precheckers": "tsc --esModuleInterop genome/*.ts dataset/*.ts",
|
|
20
|
+
"checkers": "webpack --config=./test/webpack.config.js && node test/emitPrepFiles.bundle.js && typia generate --input shared/checkers-raw --output shared/checkers",
|
|
21
|
+
"pretest:type": "npm run checkers",
|
|
22
|
+
"test:type": "webpack --env NODE_ENV=test exportsFilename=type-test-context.js && node --enable-source-maps test/serverTests.js",
|
|
23
|
+
"pretest:unit": "npm run precheckers",
|
|
24
|
+
"test:unit": "webpack --env NODE_ENV=test exportsFilename=unit-test-context.js && node --enable-source-maps test/serverTests.js",
|
|
20
25
|
"pretest:integration": "tsc --esModuleInterop genome/*.ts dataset/*.ts",
|
|
21
26
|
"test:integration": "echo 'TODO: server integration tests'",
|
|
22
27
|
"prepack": "tsc --esModuleInterop genome/*.ts dataset/*.ts && webpack --env NODE_ENV=production",
|
|
23
28
|
"response": "nodemon modules/test/test.server.js --watch src",
|
|
24
29
|
"getconf": "../build/getConfigProp.js",
|
|
25
|
-
"
|
|
26
|
-
"doc": "typedoc --json ../public/docs/server.json",
|
|
27
|
-
"precheckers": "npx ts-node ./augen/cli.js typeCheckers $PWD/routes ../../../routes > shared/checkers/raw/index.ts",
|
|
28
|
-
"checkers": "typia generate --input shared/checkers/raw --output shared/checkers/transformed --project ./shared/checkers/tsconfig.json"
|
|
30
|
+
"doc": "../augen/build.sh routes shared/types/routes shared/checkers ../public/docs/server"
|
|
29
31
|
},
|
|
30
32
|
"author": "",
|
|
31
33
|
"license": "SEE LICENSE IN ./LICENSE",
|
|
@@ -45,7 +47,6 @@
|
|
|
45
47
|
"ts-node": "^10.9.1",
|
|
46
48
|
"ts-patch": "^3.0.2",
|
|
47
49
|
"typedoc": "^0.24.8",
|
|
48
|
-
"typedoc-plugin-replace-text": "^3.1.0",
|
|
49
50
|
"typescript": "^5.0.3",
|
|
50
51
|
"typia": "^4.1.14",
|
|
51
52
|
"webpack": "^5.76.0",
|
|
@@ -54,7 +55,8 @@
|
|
|
54
55
|
"webpack-notifier": "^1.15.0"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"@sjcrh/
|
|
58
|
+
"@sjcrh/augen": "2.27.0",
|
|
59
|
+
"@sjcrh/proteinpaint-rust": "2.27.0",
|
|
58
60
|
"better-sqlite3": "^7.5.3",
|
|
59
61
|
"body-parser": "^1.15.2",
|
|
60
62
|
"canvas": "~2.9.3",
|
package/routes/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Server Routes
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
This directory contains files that specify server route APIs. By following this guidelines,
|
|
6
|
+
the auto-generation of server routes, tests, and API documentation will work as expected.
|
|
7
|
+
|
|
8
|
+
## Guidelines
|
|
9
|
+
|
|
10
|
+
### 1. Use Express to do most of the routing logic
|
|
11
|
+
|
|
12
|
+
- decentralize the route handling code into smaller, independent handler functions
|
|
13
|
+
- common request processing logic, like genome, dataset, termdb set-up should be imported
|
|
14
|
+
from a shared helper module that is common to a group of routes, or for more advanced cases,
|
|
15
|
+
moved to a [router-level middleware](https://expressjs.com/en/guide/using-middleware.html#middleware.router)
|
|
16
|
+
|
|
17
|
+
### 2. Export an `api` from the route file
|
|
18
|
+
|
|
19
|
+
Use the code from other files in this directory as examples
|
|
20
|
+
|
|
21
|
+
TODO: define the `api` type
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// work-in-progress
|
|
25
|
+
type RouteApi {
|
|
26
|
+
[key as methods]: RouteApiMethod
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type methods = 'get' | 'post'
|
|
30
|
+
|
|
31
|
+
type initArg = {
|
|
32
|
+
app?: any // Express app instance
|
|
33
|
+
genome: any // `Genome` from shared/types/genome.ts
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
@param
|
|
38
|
+
*/
|
|
39
|
+
type RouteApiMethod = {
|
|
40
|
+
endpoint: string
|
|
41
|
+
init: (initArg) => void
|
|
42
|
+
request: {
|
|
43
|
+
typeId: string
|
|
44
|
+
body?: any // specific to the route
|
|
45
|
+
}
|
|
46
|
+
response: {
|
|
47
|
+
typeId: string
|
|
48
|
+
header?: {
|
|
49
|
+
status: number
|
|
50
|
+
}
|
|
51
|
+
body?: any // specific to the route
|
|
52
|
+
}
|
|
53
|
+
examples: RouteExample[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type RouteExample = {
|
|
57
|
+
request: {
|
|
58
|
+
body?: any
|
|
59
|
+
}
|
|
60
|
+
response?: {
|
|
61
|
+
header: {
|
|
62
|
+
status: number
|
|
63
|
+
}
|
|
64
|
+
body?: any
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
### 3. Use the appropriate [HTTP response code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
|
|
69
|
+
|
|
70
|
+
This is a best practice especially for error responses. Use `res.status(code)` to set the error code.
|
|
71
|
+
This convention helps with error troubleshooting. Examples:
|
|
72
|
+
- Status `400` 'Bad Request', something is wrong with the http request payload
|
|
73
|
+
- Status `401` 'Unauthorized', the user must authenticate. The `server/src/auth.js` sets this status code
|
|
74
|
+
- Status `403` 'Forbidden', the user is authenticated/signed-in, but is not permitted to access the requested data
|
|
75
|
+
- Status `404` 'Not Found' for genome, dataset, or other data that is not found
|
|
76
|
+
- Status `500` 'Server Error' for errors related to the server process or host machine, such as the GDC API
|
|
77
|
+
not being available. Do not use code=`500` for errors that are related to specific request handler or data processing functions.
|
|
78
|
+
|
|
79
|
+
### 4. Auto-generate
|
|
80
|
+
|
|
81
|
+
- the server code will detect the routes in `server/src/run.sh`
|
|
82
|
+
- `npm run doc` to see the documented routes in http://localhost:3000/server.html
|
|
83
|
+
- `./augen/readme.sh > public/docs/readme.json` for content in http://localhost:3000/readme.html
|
|
84
|
+
|
package/routes/burden.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { BurdenRequest, BurdenResponse } from '#shared/types/routes/burden.ts'
|
|
2
|
+
import lines2R from '#src/lines2R.js'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import serverconfig from '#src/serverconfig.js'
|
|
5
|
+
import { write_file } from '#src/utils.js'
|
|
6
|
+
|
|
7
|
+
export const api = {
|
|
8
|
+
endpoint: 'burden',
|
|
9
|
+
methods: {
|
|
10
|
+
get: {
|
|
11
|
+
init({ genomes }) {
|
|
12
|
+
return async (req: any, res: any): Promise<void> => {
|
|
13
|
+
try {
|
|
14
|
+
const genome = genomes[req.query.genome]
|
|
15
|
+
if (!genome) throw `invalid q.genome=${req.query.genome}`
|
|
16
|
+
const q = req.query as BurdenRequest
|
|
17
|
+
const ds = genome.datasets[q.dslabel]
|
|
18
|
+
if (!ds) throw `invalid q.genome=${req.query.dslabel}`
|
|
19
|
+
if (!ds.cohort.cumburden?.files) throw `missing ds.cohort.cumburden.files`
|
|
20
|
+
|
|
21
|
+
const estimates = await getBurdenEstimates(req, ds)
|
|
22
|
+
const { keys, rows } = formatPayload(estimates)
|
|
23
|
+
res.send({ status: 'ok', keys, rows } as BurdenResponse)
|
|
24
|
+
} catch (e: any) {
|
|
25
|
+
res.send({ status: 'error', error: e.message || e })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
request: {
|
|
30
|
+
typeId: 'BurdenRequest'
|
|
31
|
+
},
|
|
32
|
+
response: {
|
|
33
|
+
typeId: 'BurdenResponse'
|
|
34
|
+
},
|
|
35
|
+
examples: [
|
|
36
|
+
{
|
|
37
|
+
request: {
|
|
38
|
+
body: {
|
|
39
|
+
genome: 'hg38',
|
|
40
|
+
// TODO: !!! use hg38-test and TermdbTest !!!
|
|
41
|
+
dslabel: 'SJLife',
|
|
42
|
+
diaggrp: 5,
|
|
43
|
+
sex: 1,
|
|
44
|
+
white: 1,
|
|
45
|
+
agedx: 1,
|
|
46
|
+
bleo: 0,
|
|
47
|
+
etop: 0,
|
|
48
|
+
cisp: 0,
|
|
49
|
+
carbo: 0,
|
|
50
|
+
steriod: 0,
|
|
51
|
+
vcr: 0,
|
|
52
|
+
hdmtx: 0,
|
|
53
|
+
itmt: 0,
|
|
54
|
+
ced: 0,
|
|
55
|
+
dox: 0,
|
|
56
|
+
heart: 0,
|
|
57
|
+
brain: 0,
|
|
58
|
+
abd: 0,
|
|
59
|
+
pelvis: 0,
|
|
60
|
+
chest: 0
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
response: {
|
|
64
|
+
header: { status: 200 }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function getBurdenEstimates(q, ds) {
|
|
73
|
+
const infile = path.join(serverconfig.cachedir, Math.random().toString() + '.json')
|
|
74
|
+
for (const k in q.query) {
|
|
75
|
+
q.query[k] = Number(q.query[k])
|
|
76
|
+
}
|
|
77
|
+
const data = Object.assign({}, defaults, q.query)
|
|
78
|
+
//console.log(40, data, JSON.stringify(data))
|
|
79
|
+
await write_file(infile, JSON.stringify(data))
|
|
80
|
+
// TODO: use the dataset location
|
|
81
|
+
const { fit, surv, sample } = ds.cohort.cumburden.files
|
|
82
|
+
if (!fit || !surv || !sample) throw `missing one or more of ds.cohort.burden.files.{fit, surv, sample}`
|
|
83
|
+
const args = [
|
|
84
|
+
infile,
|
|
85
|
+
`${serverconfig.tpmasterdir}/${fit}`,
|
|
86
|
+
`${serverconfig.tpmasterdir}/${surv}`,
|
|
87
|
+
`${serverconfig.tpmasterdir}/${sample}`
|
|
88
|
+
]
|
|
89
|
+
const Routput = await lines2R(path.join(serverconfig.binpath, 'utils/burden.R'), [], args)
|
|
90
|
+
const estimates = JSON.parse(Routput[0])
|
|
91
|
+
return estimates
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function formatPayload(estimates) {
|
|
95
|
+
const rawKeys = Object.keys(estimates[0])
|
|
96
|
+
const outKeys = [] as string[]
|
|
97
|
+
const keys = [] as string[]
|
|
98
|
+
for (const k of rawKeys) {
|
|
99
|
+
if (k == 'chc') {
|
|
100
|
+
keys.push(k)
|
|
101
|
+
outKeys.push(k)
|
|
102
|
+
} else {
|
|
103
|
+
const age = Number(k.slice(1).split(',')[0])
|
|
104
|
+
if (age <= 60 && age % 2 == 0) {
|
|
105
|
+
keys.push(k)
|
|
106
|
+
outKeys.push(`burden${age}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const rows = [] as number[][]
|
|
111
|
+
// v = an array of objects with age as keys as cumulative burden as value for a given CHC
|
|
112
|
+
for (const v of estimates) {
|
|
113
|
+
rows.push(keys.map(k => v[k]))
|
|
114
|
+
}
|
|
115
|
+
return { keys: outKeys, rows }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const defaults = Object.freeze({
|
|
119
|
+
diaggrp: 5,
|
|
120
|
+
sex: 0,
|
|
121
|
+
white: 1,
|
|
122
|
+
agedx: 1,
|
|
123
|
+
// chemotherapy
|
|
124
|
+
steriod: 0,
|
|
125
|
+
bleo: 0,
|
|
126
|
+
vcr: 0, //12, // Vincristine
|
|
127
|
+
etop: 0, //2500, // Etoposide
|
|
128
|
+
itmt: 0, // Intrathecal methothrexate_grp: 0,
|
|
129
|
+
ced: 0, //1.6, // Cyclophosphamide, 0.7692 mean 7692.
|
|
130
|
+
cisp: 0, //300, // Cisplatin
|
|
131
|
+
dox: 0, // Anthracycline, 3 mean 300 ml/m2
|
|
132
|
+
carbo: 0, // Carboplatin
|
|
133
|
+
hdmtx: 0, // High-Dose Methotrexate
|
|
134
|
+
// radiation
|
|
135
|
+
brain: 0, //5.4,
|
|
136
|
+
chest: 0, //2.4,
|
|
137
|
+
heart: 0,
|
|
138
|
+
pelvis: 0,
|
|
139
|
+
abd: 0 //2.4
|
|
140
|
+
})
|
package/routes/gdcMaf.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { GdcMafResponse, File } from '#shared/types/routes/gdcMaf.ts'
|
|
2
|
+
import { fileSize } from '#shared/fileSize.js'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import got from 'got'
|
|
5
|
+
|
|
6
|
+
const apihost = process.env.PP_GDC_HOST || 'https://api.gdc.cancer.gov'
|
|
7
|
+
|
|
8
|
+
export const api = {
|
|
9
|
+
endpoint: 'gdcMaf',
|
|
10
|
+
methods: {
|
|
11
|
+
get: {
|
|
12
|
+
init({ genomes }) {
|
|
13
|
+
// genomes parameter is not used
|
|
14
|
+
// could be used later to verify hg38/GDC is on this instance and otherwise disable this route..
|
|
15
|
+
|
|
16
|
+
return async (req: any, res: any): Promise<void> => {
|
|
17
|
+
try {
|
|
18
|
+
const files = await listMafFiles(req)
|
|
19
|
+
const payload = { files } as GdcMafResponse
|
|
20
|
+
res.send(payload)
|
|
21
|
+
} catch (e: any) {
|
|
22
|
+
res.send({ status: 'error', error: e.message || e })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
request: {
|
|
27
|
+
typeId: null
|
|
28
|
+
//valid: default to type checker
|
|
29
|
+
},
|
|
30
|
+
response: {
|
|
31
|
+
typeId: 'GdcMafResponse'
|
|
32
|
+
// will combine this with type checker
|
|
33
|
+
//valid: (t) => {}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
req.query {
|
|
41
|
+
filter0 // optional gdc GFF cohort filter, invisible and read only
|
|
42
|
+
}
|
|
43
|
+
*/
|
|
44
|
+
async function listMafFiles(req: any) {
|
|
45
|
+
const filters = {
|
|
46
|
+
op: 'and',
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
op: '=',
|
|
50
|
+
content: { field: 'data_format', value: 'MAF' }
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (req.query.filter0) {
|
|
56
|
+
filters.content.push(req.query.filter0)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }
|
|
60
|
+
|
|
61
|
+
const data = {
|
|
62
|
+
filters,
|
|
63
|
+
size: 1000,
|
|
64
|
+
fields: [
|
|
65
|
+
'id',
|
|
66
|
+
'file_size',
|
|
67
|
+
'experimental_strategy',
|
|
68
|
+
'cases.submitter_id', // used when listing all cases & files
|
|
69
|
+
//'associated_entities.entity_submitter_id', // semi human readable
|
|
70
|
+
//'associated_entities.case_id', // case uuid
|
|
71
|
+
'cases.samples.sample_type',
|
|
72
|
+
'analysis.workflow_type' // to drop out those as skip_workflow_type
|
|
73
|
+
].join(',')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const response = await got.post(path.join(apihost, 'files'), { headers, body: JSON.stringify(data) })
|
|
77
|
+
|
|
78
|
+
let re
|
|
79
|
+
try {
|
|
80
|
+
re = JSON.parse(response.body)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
throw 'invalid JSON from ' + api.endpoint
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(re.data?.hits)) throw 're.data.hits[] not array'
|
|
85
|
+
|
|
86
|
+
// flatten api return to table row objects
|
|
87
|
+
// it is possible to set a max size limit to limit the number of files passed to client
|
|
88
|
+
const files = [] as File[]
|
|
89
|
+
for (const h of re.data.hits) {
|
|
90
|
+
const file = {
|
|
91
|
+
id: h.id,
|
|
92
|
+
workflow_type: h.analysis?.workflow_type,
|
|
93
|
+
experimental_strategy: h.experimental_strategy,
|
|
94
|
+
file_size: fileSize(h.file_size)
|
|
95
|
+
} as File
|
|
96
|
+
const c = h.cases?.[0]
|
|
97
|
+
if (c) {
|
|
98
|
+
file.case_submitter_id = c.submitter_id
|
|
99
|
+
if (c.samples) {
|
|
100
|
+
file.sample_types = c.samples.map(i => i.sample_type).join(', ')
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
files.push(file)
|
|
104
|
+
}
|
|
105
|
+
return files
|
|
106
|
+
}
|
package/routes/genelookup.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { getResult } from '#src/gene.js'
|
|
2
|
+
import { GeneLookupRequest, GeneLookupResponse } from '#shared/types/routes/genelookup.ts'
|
|
2
3
|
|
|
3
4
|
function init({ genomes }) {
|
|
4
5
|
return (req: any, res: any): void => {
|
|
5
6
|
try {
|
|
7
|
+
const q = req.query as GeneLookupRequest
|
|
6
8
|
const g = genomes[req.query.genome]
|
|
7
9
|
if (!g) throw 'invalid genome name'
|
|
8
|
-
|
|
10
|
+
const result = getResult(g, req.query) as GeneLookupResponse
|
|
11
|
+
res.send(result)
|
|
9
12
|
} catch (e: any) {
|
|
10
13
|
res.send({ error: e.message || e })
|
|
11
14
|
if (e.stack) console.log(e.stack)
|
|
@@ -45,14 +48,3 @@ export const api: any = {
|
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
|
-
|
|
49
|
-
export type GeneLookupRequest = {
|
|
50
|
-
input: string
|
|
51
|
-
genome: string
|
|
52
|
-
deep: boolean
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export type GeneLookupResponse = {
|
|
56
|
-
error?: string
|
|
57
|
-
hits: string[]
|
|
58
|
-
}
|
package/routes/healthcheck.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { getStat } from '#src/health.ts'
|
|
2
|
+
import { HealthCheckResponse } from '#shared/types/routes/healthcheck.ts'
|
|
2
3
|
|
|
3
4
|
export const api = {
|
|
4
5
|
endpoint: 'healthcheck',
|
|
5
6
|
methods: {
|
|
6
7
|
get: {
|
|
7
8
|
init({ genomes }) {
|
|
8
|
-
return async (req:
|
|
9
|
+
return async (req: undefined, res: any): Promise<void> => {
|
|
9
10
|
try {
|
|
10
|
-
const health = await getStat(genomes)
|
|
11
|
+
const health = (await getStat(genomes)) as HealthCheckResponse
|
|
11
12
|
res.send(health)
|
|
12
13
|
} catch (e: any) {
|
|
13
14
|
res.send({ status: 'error', error: e.message || e })
|
|
@@ -26,47 +27,3 @@ export const api = {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* for documentation only, to signify integer: not type-checked statically
|
|
32
|
-
*/
|
|
33
|
-
type int = number
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Information aboute the server build version and dates,
|
|
37
|
-
* including the date when the server was last launched
|
|
38
|
-
*/
|
|
39
|
-
export type VersionInfo = {
|
|
40
|
-
pkgver: string
|
|
41
|
-
codedate: string
|
|
42
|
-
launchdate: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
type BuildByGenome = {
|
|
46
|
-
[index: string]: GenomeBuildInfo
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export type GenomeBuildInfo = {
|
|
50
|
-
genedb: DbInfo
|
|
51
|
-
termdbs?: {
|
|
52
|
-
[index: string]: DbInfo
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type DbInfo = {
|
|
57
|
-
buildDate: string // "unknown" or a Date-convertible string
|
|
58
|
-
tables?: {
|
|
59
|
-
[index: string]: int
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Server sttus and data related to it's health
|
|
65
|
-
*/
|
|
66
|
-
export type HealthCheckResponse = {
|
|
67
|
-
status: 'ok' | 'error'
|
|
68
|
-
genomes: BuildByGenome
|
|
69
|
-
versionInfo: VersionInfo
|
|
70
|
-
w?: number[]
|
|
71
|
-
rs?: number
|
|
72
|
-
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { trigger_getViolinPlotData } from '#src/termdb.violin.js'
|
|
2
|
+
// import { getViolinRequest, getViolinResponse } from '#shared/types/routes/termdb.violin'
|
|
3
|
+
|
|
4
|
+
export const api: any = {
|
|
5
|
+
endpoint: 'termdb/violin',
|
|
6
|
+
methods: {
|
|
7
|
+
get: {
|
|
8
|
+
init,
|
|
9
|
+
request: {
|
|
10
|
+
typeId: 'getViolinRequest'
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
typeId: 'getViolinResponse'
|
|
14
|
+
},
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
request: {
|
|
18
|
+
body: {
|
|
19
|
+
genome: 'hg38-test',
|
|
20
|
+
dslabel: 'TermdbTest',
|
|
21
|
+
embedder: 'localhost',
|
|
22
|
+
devicePixelRatio: 2.200000047683716,
|
|
23
|
+
maxThickness: 150,
|
|
24
|
+
screenThickness: 1218,
|
|
25
|
+
filter: {
|
|
26
|
+
type: 'tvslst',
|
|
27
|
+
in: true,
|
|
28
|
+
join: '',
|
|
29
|
+
lst: [
|
|
30
|
+
{
|
|
31
|
+
tag: 'cohortFilter',
|
|
32
|
+
type: 'tvs',
|
|
33
|
+
tvs: { term: { id: 'subcohort', type: 'categorical' }, values: [{ key: 'ABC', label: 'ABC' }] }
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
svgw: 227.27272234672367,
|
|
38
|
+
orientation: 'horizontal',
|
|
39
|
+
datasymbol: 'bean',
|
|
40
|
+
radius: 5,
|
|
41
|
+
strokeWidth: 0.2,
|
|
42
|
+
axisHeight: 60,
|
|
43
|
+
rightMargin: 50,
|
|
44
|
+
unit: 'abs',
|
|
45
|
+
plotThickness: 150,
|
|
46
|
+
termid: 'agedx'
|
|
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 getViolinRequest
|
|
65
|
+
try {
|
|
66
|
+
const g = genomes[req.query.genome]
|
|
67
|
+
const ds = g.datasets[req.query.dslabel]
|
|
68
|
+
if (!g) throw 'invalid genome name'
|
|
69
|
+
const data = await trigger_getViolinPlotData(req.query, null, ds, g) // as getViolinResponse
|
|
70
|
+
res.send(data)
|
|
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
|
+
}
|