@sjcrh/proteinpaint-server 2.39.4 → 2.39.6
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 +1 -1
- package/routes/gdc.topMutatedGenes.ts +278 -40
- package/server.js +1 -1
- package/server.js.map +1 -1
- package/shared/common.js +6 -0
- package/src/mds3.gdc.filter.js +8 -4
package/package.json
CHANGED
|
@@ -1,62 +1,299 @@
|
|
|
1
|
-
import { GdcTopMutatedGeneResponse } from '#shared/types/routes/gdc.topMutatedGenes.ts'
|
|
2
|
-
import
|
|
1
|
+
import { GdcTopMutatedGeneRequest, GdcTopMutatedGeneResponse, Gene } from '#shared/types/routes/gdc.topMutatedGenes.ts'
|
|
2
|
+
import { mclasscnvgain, mclasscnvloss, dtsnvindel } from '#shared/common.js'
|
|
3
3
|
import got from 'got'
|
|
4
4
|
|
|
5
|
+
// TODO change to /termdb/topMutatedGenes
|
|
6
|
+
|
|
5
7
|
const apihost = process.env.PP_GDC_HOST || 'https://api.gdc.cancer.gov'
|
|
8
|
+
const apihostGraphql = apihost + (apihost.includes('/v0') ? '' : '/v0') + '/graphql'
|
|
6
9
|
|
|
7
10
|
export const api = {
|
|
8
11
|
endpoint: 'gdc/topMutatedGenes',
|
|
9
12
|
methods: {
|
|
10
|
-
|
|
11
|
-
init
|
|
12
|
-
/*
|
|
13
|
-
genomes parameter is currently not used
|
|
14
|
-
could be used later to:
|
|
15
|
-
- verify hg38/GDC is on this instance and otherwise disable this route..
|
|
16
|
-
- perform conversion on gene name/id for future on needs
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
return async (req: any, res: any): Promise<void> => {
|
|
20
|
-
try {
|
|
21
|
-
const genes = await getGenes(req.query)
|
|
22
|
-
const payload = { genes } as GdcTopMutatedGeneResponse
|
|
23
|
-
res.send(payload)
|
|
24
|
-
} catch (e: any) {
|
|
25
|
-
res.send({ status: 'error', error: e.message || e })
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
},
|
|
13
|
+
all: {
|
|
14
|
+
init,
|
|
29
15
|
request: {
|
|
30
|
-
typeId:
|
|
31
|
-
//valid: default to type checker
|
|
16
|
+
typeId: 'GdcTopMutatedGeneRequest'
|
|
32
17
|
},
|
|
33
18
|
response: {
|
|
34
19
|
typeId: 'GdcTopMutatedGeneResponse'
|
|
35
|
-
// will combine this with type checker
|
|
36
|
-
//valid: (t) => {}
|
|
37
20
|
}
|
|
38
21
|
}
|
|
39
22
|
}
|
|
40
23
|
}
|
|
41
24
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
25
|
+
function init() {
|
|
26
|
+
/*
|
|
27
|
+
genomes parameter is currently not used
|
|
28
|
+
could be used later to:
|
|
29
|
+
- verify hg38/GDC is on this instance and otherwise disable this route..
|
|
30
|
+
- perform conversion on gene name/id for future on needs
|
|
31
|
+
*/
|
|
32
|
+
return async (req: any, res: any): Promise<void> => {
|
|
33
|
+
const q: GdcTopMutatedGeneRequest = req.query
|
|
34
|
+
try {
|
|
35
|
+
const genes = await getGenesGraphql(q)
|
|
36
|
+
const payload: GdcTopMutatedGeneResponse = { genes }
|
|
37
|
+
res.send(payload)
|
|
38
|
+
} catch (e: any) {
|
|
39
|
+
res.send({ status: 'error', error: e.message || e })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
const query = `
|
|
45
|
+
query GenesTable_relayQuery(
|
|
46
|
+
$genesTable_filters: FiltersArgument
|
|
47
|
+
$genesTable_size: Int
|
|
48
|
+
$genesTable_offset: Int
|
|
49
|
+
$score: String
|
|
50
|
+
$ssmCase: FiltersArgument
|
|
51
|
+
$geneCaseFilter: FiltersArgument
|
|
52
|
+
$ssmTested: FiltersArgument
|
|
53
|
+
$cnvTested: FiltersArgument
|
|
54
|
+
$cnvGainFilters: FiltersArgument
|
|
55
|
+
$cnvLossFilters: FiltersArgument
|
|
56
|
+
) {
|
|
57
|
+
genesTableViewer: viewer {
|
|
58
|
+
explore {
|
|
59
|
+
cases {
|
|
60
|
+
hits(first: 0, filters: $ssmTested) {
|
|
61
|
+
total
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
filteredCases: cases {
|
|
65
|
+
hits(first: 0, filters: $geneCaseFilter) {
|
|
66
|
+
total
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
cnvCases: cases {
|
|
70
|
+
hits(first: 0, filters: $cnvTested) {
|
|
71
|
+
total
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
genes {
|
|
75
|
+
hits(first: $genesTable_size, offset: $genesTable_offset, filters: $genesTable_filters, score: $score) {
|
|
76
|
+
total
|
|
77
|
+
edges {
|
|
78
|
+
node {
|
|
79
|
+
id
|
|
80
|
+
numCases: score
|
|
81
|
+
symbol
|
|
82
|
+
name
|
|
83
|
+
cytoband
|
|
84
|
+
biotype
|
|
85
|
+
gene_id
|
|
86
|
+
is_cancer_gene_census
|
|
87
|
+
ssm_case: case {
|
|
88
|
+
hits(first: 0, filters: $ssmCase) {
|
|
89
|
+
total
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
cnv_case: case {
|
|
93
|
+
hits(first: 0, filters: $cnvTested) {
|
|
94
|
+
total
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
case_cnv_gain: case {
|
|
98
|
+
hits(first: 0, filters: $cnvGainFilters) {
|
|
99
|
+
total
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
case_cnv_loss: case {
|
|
103
|
+
hits(first: 0, filters: $cnvLossFilters) {
|
|
104
|
+
total
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`
|
|
115
|
+
|
|
116
|
+
async function getGenesGraphql(q: GdcTopMutatedGeneRequest) {
|
|
117
|
+
// set type "any" to avoid complains
|
|
118
|
+
const variables: any = {
|
|
119
|
+
genesTable_filters: {
|
|
120
|
+
op: 'and',
|
|
121
|
+
content: []
|
|
122
|
+
},
|
|
123
|
+
genesTable_size: q.maxGenes || 50,
|
|
124
|
+
genesTable_offset: 0,
|
|
125
|
+
score: 'case.project.project_id',
|
|
126
|
+
ssmCase: {
|
|
127
|
+
op: 'and',
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
op: 'in',
|
|
131
|
+
content: {
|
|
132
|
+
field: 'cases.available_variation_data',
|
|
133
|
+
value: ['ssm']
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
op: 'NOT',
|
|
138
|
+
content: {
|
|
139
|
+
field: 'genes.case.ssm.observation.observation_id',
|
|
140
|
+
value: 'MISSING'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
geneCaseFilter: {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
content: {
|
|
149
|
+
field: 'cases.available_variation_data',
|
|
150
|
+
value: ['ssm']
|
|
151
|
+
},
|
|
152
|
+
op: 'in'
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
op: 'and'
|
|
156
|
+
},
|
|
157
|
+
ssmTested: {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
content: {
|
|
161
|
+
field: 'cases.available_variation_data',
|
|
162
|
+
value: ['ssm']
|
|
163
|
+
},
|
|
164
|
+
op: 'in'
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
op: 'and'
|
|
168
|
+
},
|
|
169
|
+
cnvTested: {
|
|
170
|
+
op: 'and',
|
|
171
|
+
content: [
|
|
172
|
+
{
|
|
173
|
+
content: {
|
|
174
|
+
field: 'cases.available_variation_data',
|
|
175
|
+
value: ['cnv']
|
|
176
|
+
},
|
|
177
|
+
op: 'in'
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
cnvGainFilters: {
|
|
182
|
+
op: 'and',
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
content: {
|
|
186
|
+
field: 'cases.available_variation_data',
|
|
187
|
+
value: ['cnv']
|
|
188
|
+
},
|
|
189
|
+
op: 'in'
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
content: {
|
|
193
|
+
field: 'cnvs.cnv_change',
|
|
194
|
+
value: ['Gain']
|
|
195
|
+
},
|
|
196
|
+
op: 'in'
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
cnvLossFilters: {
|
|
201
|
+
op: 'and',
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
content: {
|
|
205
|
+
field: 'cases.available_variation_data',
|
|
206
|
+
value: ['cnv']
|
|
207
|
+
},
|
|
208
|
+
op: 'in'
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
content: {
|
|
212
|
+
field: 'cnvs.cnv_change',
|
|
213
|
+
value: ['Loss']
|
|
214
|
+
},
|
|
215
|
+
op: 'in'
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
56
221
|
if (q.filter0) {
|
|
57
|
-
|
|
58
|
-
|
|
222
|
+
variables.genesTable_filters.content.push(JSON.parse(JSON.stringify(q.filter0)))
|
|
223
|
+
variables.geneCaseFilter.content.push(JSON.parse(JSON.stringify(q.filter0)))
|
|
224
|
+
variables.cnvTested.content.push(JSON.parse(JSON.stringify(q.filter0)))
|
|
225
|
+
variables.cnvGainFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
|
|
226
|
+
variables.cnvLossFilters.content.push(JSON.parse(JSON.stringify(q.filter0)))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (q.geneFilter == 'CGC') {
|
|
230
|
+
variables.genesTable_filters.content.push(geneCGC())
|
|
231
|
+
variables.geneCaseFilter.content.push(geneCGC())
|
|
232
|
+
variables.cnvTested.content.push(geneCGC())
|
|
233
|
+
variables.cnvGainFilters.content.push(geneCGC())
|
|
234
|
+
variables.cnvLossFilters.content.push(geneCGC())
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const response = await got.post(apihostGraphql, {
|
|
238
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
239
|
+
body: JSON.stringify({ query, variables })
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const re: any = JSON.parse(response.body)
|
|
243
|
+
const genes: Gene[] = []
|
|
244
|
+
for (const g of re.data.genesTableViewer.explore.genes.hits.edges) {
|
|
245
|
+
/* g.node is:
|
|
246
|
+
{
|
|
247
|
+
"biotype": "protein_coding",
|
|
248
|
+
"case_cnv_gain": { "hits": { "total": 65 } },
|
|
249
|
+
"case_cnv_loss": { "hits": { "total": 93 } },
|
|
250
|
+
"cnv_case": { "hits": { "total": 173 } },
|
|
251
|
+
"cytoband": [
|
|
252
|
+
"12q15"
|
|
253
|
+
],
|
|
254
|
+
"gene_id": "ENSG00000127329",
|
|
255
|
+
"is_cancer_gene_census": true,
|
|
256
|
+
"name": "protein tyrosine phosphatase receptor type B",
|
|
257
|
+
"numCases": 18,
|
|
258
|
+
"ssm_case": { "hits": { "total": 630 } },
|
|
259
|
+
"symbol": "PTPRB"
|
|
260
|
+
}
|
|
261
|
+
*/
|
|
262
|
+
if (typeof g.node != 'object') throw 'node missing from re.data.genesTableViewer.explore.genes.hits.edges[]'
|
|
263
|
+
const mutationStat: any = []
|
|
264
|
+
if (Number.isInteger(g.node.case_cnv_gain?.hits?.total) && g.node.case_cnv_gain.hits.total > 0)
|
|
265
|
+
mutationStat.push({ class: mclasscnvgain, count: g.node.case_cnv_gain.hits.total })
|
|
266
|
+
if (Number.isInteger(g.node.case_cnv_loss?.hits?.total) && g.node.case_cnv_loss.hits.total > 0)
|
|
267
|
+
mutationStat.push({ class: mclasscnvloss, count: g.node.case_cnv_loss.hits.total })
|
|
268
|
+
if (Number.isInteger(g.node.ssm_case?.hits?.total) && g.node.ssm_case.hits.total > 0)
|
|
269
|
+
mutationStat.push({ dt: dtsnvindel, count: g.node.ssm_case.hits.total })
|
|
270
|
+
genes.push({
|
|
271
|
+
gene: g.node.symbol,
|
|
272
|
+
mutationStat
|
|
273
|
+
})
|
|
59
274
|
}
|
|
275
|
+
return genes
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function geneCGC() {
|
|
279
|
+
// return a copy of cgc filter obj each time
|
|
280
|
+
return {
|
|
281
|
+
content: {
|
|
282
|
+
field: 'genes.is_cancer_gene_census',
|
|
283
|
+
value: ['true']
|
|
284
|
+
},
|
|
285
|
+
op: 'in'
|
|
286
|
+
} as object
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/*************************************
|
|
290
|
+
below are old code
|
|
291
|
+
old method to use rest api
|
|
292
|
+
**************************************
|
|
293
|
+
this api only gets ssm-cases and does not account for cnv cases, will not return any gene for ssm-less cohort e.g. APOLLO-LUAD
|
|
294
|
+
thus is replaced by getGenesGraphql
|
|
295
|
+
async function getGenes(q: GdcTopMutatedGeneRequest) {
|
|
296
|
+
const _f = q.filter0 || { op: 'and', content: [] } // allow blank filter to test geneset edit ui (without filter)
|
|
60
297
|
const response = await got.post(path.join(apihost, '/analysis/top_mutated_genes_by_project'), {
|
|
61
298
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
62
299
|
body: JSON.stringify({
|
|
@@ -73,6 +310,7 @@ async function getGenes(q: any) {
|
|
|
73
310
|
}
|
|
74
311
|
return genes
|
|
75
312
|
}
|
|
313
|
+
*/
|
|
76
314
|
|
|
77
315
|
/*
|
|
78
316
|
str:
|
|
@@ -89,7 +327,6 @@ geneFilter: str
|
|
|
89
327
|
}
|
|
90
328
|
]
|
|
91
329
|
}
|
|
92
|
-
*/
|
|
93
330
|
function mayAddCGC2filter(f: any, geneFilter?: string) {
|
|
94
331
|
// reformulate f into f2
|
|
95
332
|
// f may be "in" or "and". f2 is always "and", in order to join in additional filters
|
|
@@ -128,3 +365,4 @@ function mayAddCGC2filter(f: any, geneFilter?: string) {
|
|
|
128
365
|
|
|
129
366
|
return f2
|
|
130
367
|
}
|
|
368
|
+
*/
|