@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.39.4",
3
+ "version": "2.39.6",
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",
@@ -1,62 +1,299 @@
1
- import { GdcTopMutatedGeneResponse } from '#shared/types/routes/gdc.topMutatedGenes.ts'
2
- import path from 'path'
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
- get: {
11
- init({ genomes }) {
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: null
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
- req.query {
44
- filter0 // optional gdc GFF cohort filter, invisible and read only
45
- FIXME should there be pp filter too?
46
- geneFilter?: str
47
- maxGenes: int
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
- mayAddCGC2filter() are copied to
51
- /utils/gdc/topSSMgenes.js
52
- and hosted on https://proteinpaint.stjude.org/GDC/
53
- */
54
- async function getGenes(q: any) {
55
- let _f = { op: 'and', content: [] } // allow blank filter to test geneset edit ui (without filter)
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
- if (typeof q.filter0 != 'object') throw 'filter0 not object'
58
- _f = q.filter0
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
+ */