diff --git a/package.json b/package.json index fe3c130a..41b97869 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "uglifyjs-webpack-plugin": "1.1.5", "web-app-manifest-loader": "0.1.1", "webpack": "3.10.0", - "webpack-dev-server": "2.9.5", + "webpack-dev-server": "^3.7.2", "webpack-merge": "4.1.1", "webpack-notifier": "1.5.1", "webpack-visualizer-plugin": "0.1.11", diff --git a/src/main/webapp/app/clinical/clinical.component.html b/src/main/webapp/app/clinical/clinical.component.html index c2d7bd23..04a2f3a3 100644 --- a/src/main/webapp/app/clinical/clinical.component.html +++ b/src/main/webapp/app/clinical/clinical.component.html @@ -21,7 +21,7 @@
-
@@ -32,10 +32,10 @@
@@ -69,7 +69,7 @@
-
@@ -80,10 +80,10 @@
diff --git a/src/main/webapp/app/clinical/clinical.component.ts b/src/main/webapp/app/clinical/clinical.component.ts index 14f7c1ac..d3ace4ca 100644 --- a/src/main/webapp/app/clinical/clinical.component.ts +++ b/src/main/webapp/app/clinical/clinical.component.ts @@ -106,13 +106,13 @@ export class ClinicalComponent implements OnInit { } onSingleSelected(option) { if (_.isUndefined(option)) { - this.clinicalInput.sub_type = ''; + this.clinicalInput.subtype = ''; } else if (option && this.clinicalInput.main_type !== this.subToMainMapping[option]) { this.clinicalInput.main_type = this.subToMainMapping[option]; } } onSingleDeselectedMaintype() { - this.clinicalInput.sub_type = ''; + this.clinicalInput.subtype = ''; this.clinicalInput.main_type = ''; } getDisplayContent(key: string) { diff --git a/src/main/webapp/app/clinical/clinical.model.ts b/src/main/webapp/app/clinical/clinical.model.ts index 5814dfdb..8da3953f 100644 --- a/src/main/webapp/app/clinical/clinical.model.ts +++ b/src/main/webapp/app/clinical/clinical.model.ts @@ -2,7 +2,7 @@ export interface Clinical { age_numerical?: string, oncotree_primary_diagnosis?: string, main_type?: string, - sub_type?: string, + subtype?: string, gender?: string, no_oncotree_primary_diagnosis?: boolean } diff --git a/src/main/webapp/app/clinical/clinical.scss b/src/main/webapp/app/clinical/clinical.scss index e02327b0..80804fa5 100644 --- a/src/main/webapp/app/clinical/clinical.scss +++ b/src/main/webapp/app/clinical/clinical.scss @@ -25,3 +25,6 @@ label { .collapse-margin{ margin: 10px 0px 10px 30px; } +.gender-radio { + margin-right: 5px; +} diff --git a/src/main/webapp/app/genomic/geneset.model.ts b/src/main/webapp/app/genomic/geneset.model.ts new file mode 100644 index 00000000..102ed865 --- /dev/null +++ b/src/main/webapp/app/genomic/geneset.model.ts @@ -0,0 +1,22 @@ +export interface Geneset { + name: string, + uuid: number, + genes: Gene[] +} + +export interface Gene { + entrezGeneId: number, + hugoSymbol: string, + name: string, + oncogene: boolean, + curatedIsoform: string, + curatedRefSeq: string, + geneAliases: string[], + tsg: boolean +} + +export interface GenesetOption { + name: string, + uuid: number, + genes: string[] +} diff --git a/src/main/webapp/app/genomic/genomic.component.html b/src/main/webapp/app/genomic/genomic.component.html index 0a477147..f9b6c06e 100644 --- a/src/main/webapp/app/genomic/genomic.component.html +++ b/src/main/webapp/app/genomic/genomic.component.html @@ -1,12 +1,26 @@
+
geneset:
+
+ + +
+
+
+
hugo_symbol:
+
+ {{selectedGenesetOption.genes.join(', ')}} +
+
+
hugo_symbol: {{ validationMessage['gene'] }}
- +
@@ -166,12 +180,26 @@
+
geneset:
+
+ + +
+
+
+
hugo_symbol:
+
+ {{selectedGenesetOption.genes.join(', ')}} +
+
+
hugo_symbol: {{ validationMessage['gene'] }}
- +
@@ -324,20 +352,42 @@
-
-
- +
+
+
+ +
+
+ {{selectedGenesetOption.name}} +
-
- {{getDisplayContent('hugo_symbol')}} +
+
+ +
+
+ {{selectedGenesetOption.genes.join(', ')}} +
-
-
- +
+
+
+ +
+
+ {{getDisplayContent('hugo_symbol')}} +
-
- {{getDisplayContent('annotated_variant')}} +
+
+
+
+ +
+
+ {{getDisplayContent('annotated_variant')}} +
diff --git a/src/main/webapp/app/genomic/genomic.component.ts b/src/main/webapp/app/genomic/genomic.component.ts index caf14ec5..89d6a7bf 100644 --- a/src/main/webapp/app/genomic/genomic.component.ts +++ b/src/main/webapp/app/genomic/genomic.component.ts @@ -7,7 +7,8 @@ import 'rxjs/add/operator/distinctUntilChanged'; import { Genomic } from './genomic.model'; import * as _ from 'lodash'; import { ConnectionService } from '../service/connection.service'; -import MainUtil from '../service/mainutil'; +import { MainutilService } from '../service/mainutil.service'; +import { Geneset, GenesetOption } from './geneset.model'; @Component({ selector: 'jhi-genomic', @@ -38,6 +39,8 @@ export class GenomicComponent implements OnInit { }; geneValidation = false; exampleValidation = false; + genesetOptions = this.trialService.getGenesetsOptions(); + selectedGenesetOption: GenesetOption; search = (text$: Observable) => text$ @@ -59,6 +62,9 @@ export class GenomicComponent implements OnInit { constructor(private trialService: TrialService, public connectionService: ConnectionService) {} ngOnInit() { + if (!_.isUndefined(this.unit['genomic']) && !_.isUndefined(this.unit['genomic']['geneset_uuid'])) { + this.selectedGenesetOption = _.find(this.genesetOptions, { uuid: this.unit['genomic']['geneset_uuid'] }); + } this.trialService.genomicInputObs.subscribe((message) => { this.genomicInput = message; }); @@ -116,4 +122,15 @@ export class GenomicComponent implements OnInit { unCheckRadio(key, event) { this.genomicInput[key] = MainUtil.uncheckRadio(this.genomicInput[key], event.target.value); } + changeGeneset() { + if (this.genomicInput.geneset_uuid) { + this.selectedGenesetOption = _.find(this.genesetOptions, { uuid: this.genomicInput.geneset_uuid }); + if (_.isEmpty(this.genomicInput.annotated_variant)) { + this.genomicInput.annotated_variant = 'Oncogenic Mutations'; + } + } else { + this.selectedGenesetOption = null; + this.genomicInput.annotated_variant = ''; + } + } } diff --git a/src/main/webapp/app/genomic/genomic.model.ts b/src/main/webapp/app/genomic/genomic.model.ts index 8622ab78..5798638b 100644 --- a/src/main/webapp/app/genomic/genomic.model.ts +++ b/src/main/webapp/app/genomic/genomic.model.ts @@ -1,5 +1,7 @@ export interface Genomic { hugo_symbol?: string, + geneset?: string, + geneset_uuid?: number, annotated_variant?: string, matching_examples?: string, germline?: string, diff --git a/src/main/webapp/app/genomic/genomic.scss b/src/main/webapp/app/genomic/genomic.scss index 69c425ff..6ac4d309 100644 --- a/src/main/webapp/app/genomic/genomic.scss +++ b/src/main/webapp/app/genomic/genomic.scss @@ -1,6 +1,9 @@ label { min-width:180px; } +input:disabled { + background-color: #cccccc; +} .selectStyle { max-width:300px; } @@ -12,6 +15,9 @@ label { margin-top: -25px; margin-left: 30px; } -.icon-margin{ +.icon-margin { margin-left: 20px; } +.germline-radio { + margin-right: 5px; +} diff --git a/src/main/webapp/app/panel/panel.component.ts b/src/main/webapp/app/panel/panel.component.ts index 4a8c2862..7b9d72af 100644 --- a/src/main/webapp/app/panel/panel.component.ts +++ b/src/main/webapp/app/panel/panel.component.ts @@ -24,7 +24,6 @@ export class PanelComponent implements OnInit { finalPath = []; message = ''; addNode = false; - moving = false; nodeOptions: Array = ['Genomic', 'Clinical', 'And', 'Or']; nodeType = ''; selectedItems = []; @@ -41,7 +40,6 @@ export class PanelComponent implements OnInit { allSubTypesOptions = this.trialService.getAllSubTypesOptions(); subToMainMapping = this.trialService.getSubToMainMapping(); mainTypesOptions = this.trialService.getMainTypesOptions(); - statusOptions = this.trialService.getStatusOptions(); nctIdChosen: string; oncokb = MainUtil.oncokb; isPermitted = MainUtil.isPermitted; @@ -180,24 +178,9 @@ export class PanelComponent implements OnInit { }); } } - hasEmptyGenomicFields(obj: any) { - let genomicFieldsToCheck = this.oncokbGenomicFields; - if (!this.oncokb) { - genomicFieldsToCheck = _.without(this.genomicFields, 'matching_examples'); - } - for (const key of genomicFieldsToCheck) { - if (!_.isUndefined(obj[key]) && obj[key].length > 0) { - return false; - } - } - return true; - } - hasEmptyClinicalFields(obj: any) { - // Check clinical input fields - // TODO: Use clinicalFields to replace the array after we remove main_type input field - const clinicalFieldsToCheck = ['age_numerical', 'sub_type', 'main_type']; - for (const key of clinicalFieldsToCheck) { - if (!_.isUndefined(obj[key]) && obj[key].length > 0) { + hasEmptyFields(obj: any) { + for (const key of _.keys(obj)) { + if (!_.isUndefined(obj[key]) && obj[key] && obj[key].length > 0) { return false; } } @@ -212,12 +195,12 @@ export class PanelComponent implements OnInit { getEmptySectionNames(type: string, emptySections: Array) { switch (type) { case 'Genomic': - if (this.hasEmptyGenomicFields(this.genomicInput)) { + if (this.hasEmptyFields(this.genomicInput)) { emptySections.push('Genomic'); } break; case 'Clinical': - if (this.hasEmptyClinicalFields(this.clinicalInput)) { + if (this.hasEmptyFields(this.clinicalInput)) { emptySections.push('Clinical'); } break; @@ -364,8 +347,8 @@ export class PanelComponent implements OnInit { } getOncotree() { let oncotree_primary_diagnosis = ''; - if (this.clinicalInput.sub_type) { - oncotree_primary_diagnosis = this.clinicalInput.sub_type; + if (this.clinicalInput.subtype) { + oncotree_primary_diagnosis = this.clinicalInput.subtype; }else if (this.clinicalInput.main_type) { oncotree_primary_diagnosis = this.clinicalInput.main_type; } @@ -374,8 +357,6 @@ export class PanelComponent implements OnInit { prepareClinicalData() { this.clinicalInput['oncotree_primary_diagnosis'] = this.getOncotree(); const clinicalToSave = _.clone(this.clinicalInput); - delete clinicalToSave['main_type']; - delete clinicalToSave['sub_type']; this.prepareSectionByField('clinical', clinicalToSave); return clinicalToSave; } @@ -385,15 +366,9 @@ export class PanelComponent implements OnInit { return genomicToSave; } prepareSectionByField(type: string, nodeData: object) { - let keysToCheck = []; - if (type === 'clinical') { - keysToCheck = this.clinicalFields; - } else if (type === 'genomic') { - keysToCheck = this.genomicFields; - } - for (const key of keysToCheck) { + for (const key of _.keys(nodeData)) { // remove empty fields - if (!_.isUndefined(nodeData[key]) && nodeData[key].length === 0) { + if (_.isUndefined(nodeData[key]) || nodeData[key] === null || nodeData[key].length === 0) { delete nodeData[key]; } // apply not logic @@ -529,7 +504,9 @@ export class PanelComponent implements OnInit { } else if (this.unit.hasOwnProperty('clinical')) { this.trialService.setClinicalInput(_.clone(this.unit['clinical'])); this.setNotLogic('clinical'); - this.setOncotree(); + if (_.isUndefined(this.clinicalInput.main_type) && _.isUndefined(this.clinicalInput.subtype)) { + this.setOncotree(); + } } else if (this.unit.hasOwnProperty('arm_description')) { const armToAdd: Arm = { arm_code: this.unit['arm_code'], @@ -548,12 +525,12 @@ export class PanelComponent implements OnInit { } setOncotree() { const oncotree_primary_diagnosis = this.clinicalInput['oncotree_primary_diagnosis']; - this.clinicalInput['sub_type'] = ''; + this.clinicalInput['subtype'] = ''; this.clinicalInput['main_type'] = ''; let isSubtype = false; for (const item of this.allSubTypesOptions) { if (item === oncotree_primary_diagnosis) { - this.clinicalInput['sub_type'] = oncotree_primary_diagnosis; + this.clinicalInput['subtype'] = oncotree_primary_diagnosis; this.clinicalInput['main_type'] = this.subToMainMapping[oncotree_primary_diagnosis]; isSubtype = true; } @@ -568,15 +545,15 @@ export class PanelComponent implements OnInit { } setNotLogic(type: string) { if (type === 'clinical') { - for (const key of this.clinicalFields) { - if (!_.isUndefined(this.clinicalInput[key]) && this.clinicalInput[key].startsWith('!')) { + for (const key of _.keys(this.clinicalInput)) { + if (!_.isUndefined(this.clinicalInput[key]) && _.isString(this.clinicalInput[key]) && this.clinicalInput[key].startsWith('!')) { this.clinicalInput['no_' + key] = true; this.clinicalInput[key] = this.clinicalInput[key].substr(1); } } } else if (type === 'genomic') { - for (const key of this.genomicFields) { - if (!_.isUndefined(this.genomicInput[key]) && this.genomicInput[key].startsWith('!')) { + for (const key of _.keys(this.genomicInput)) { + if (!_.isUndefined(this.genomicInput[key]) && _.isString(this.genomicInput[key]) && this.genomicInput[key].startsWith('!')) { this.genomicInput['no_' + key] = true; this.genomicInput[key] = this.genomicInput[key].substr(1); } diff --git a/src/main/webapp/app/service/connection.service.ts b/src/main/webapp/app/service/connection.service.ts index cea38fb0..03350fc9 100644 --- a/src/main/webapp/app/service/connection.service.ts +++ b/src/main/webapp/app/service/connection.service.ts @@ -21,6 +21,8 @@ export class ConnectionService { return 'http://oncotree.mskcc.org/api/tumorTypes/search'; case 'OncoKBVariant': return 'http://oncokb.org/api/v1/variants'; + case 'Genesets': + return 'http://oncokb.org/api/v1/genesets'; case 'GeneValidation': return 'http://mygene.info/v3/query?species=human&q=symbol:'; case 'ClinicalTrials': @@ -38,6 +40,8 @@ export class ConnectionService { return SERVER_API_URL + 'proxy/http/oncotree.mskcc.org/api/tumorTypes/search'; case 'OncoKBVariant': return SERVER_API_URL + 'proxy/http/oncokb.org/api/v1/variants'; + case 'Genesets': + return SERVER_API_URL + 'proxy/http/oncokb.org/api/v1/genesets'; case 'GeneValidation': return SERVER_API_URL + 'proxy/http/mygene.info/v3/query?species=human&q=symbol:'; case 'ClinicalTrials': @@ -81,4 +85,11 @@ export class ConnectionService { getDrugs(query: string) { return this.http.get(this.getAPIUrl('Drugs') + `?name=${query}`); } + + getGenesets() { + return this.http.get(this.getAPIUrl('Genesets')); + } + getGenesetById(uuid: number) { + return this.http.get(this.getAPIUrl('Genesets') + `/${uuid}`); + } } diff --git a/src/main/webapp/app/service/trial.service.ts b/src/main/webapp/app/service/trial.service.ts index 5c54d0ac..cbe80b03 100644 --- a/src/main/webapp/app/service/trial.service.ts +++ b/src/main/webapp/app/service/trial.service.ts @@ -13,6 +13,7 @@ import { of } from 'rxjs/observable/of'; import { catchError, map } from 'rxjs/operators'; import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database'; import MainUtil from './mainutil'; +import { Gene, Geneset, GenesetOption } from '../genomic/geneset.model'; @Injectable() export class TrialService { @@ -68,6 +69,7 @@ export class TrialService { subTypesOptions = {}; allSubTypesOptions = []; + genesetsOptions: GenesetOption[] = []; subToMainMapping = {}; mainTypesOptions = ['All Solid Tumors', 'All Liquid Tumors', 'All Tumors', 'All Pediatric Tumors']; statusOptions = ['Active', 'Administratively Complete', 'Approved', 'Closed', 'Closed to Accrual', @@ -145,6 +147,18 @@ export class TrialService { this.annotated_variants[key].sort(); } }); + + // prepare genesets list + this.connectionService.getGenesets().subscribe((res: Geneset[]) => { + for (const geneset of res) { + const genesetOption: GenesetOption = { + uuid: geneset.uuid, + name: geneset.name, + genes: _.map(geneset.genes, (gene: Gene) => gene.hugoSymbol) + }; + this.genesetsOptions.push(genesetOption); + } + }); } fetchTrials() { this.trialsRef.snapshotChanges().subscribe((action) => { @@ -239,6 +253,9 @@ export class TrialService { getSubTypesOptions() { return this.subTypesOptions; } + getGenesetsOptions() { + return this.genesetsOptions; + } loadDrugsOptions(query: string) { // prepare drugs list return this.connectionService.getDrugs(query).pipe(