From 7cf140d75b1ea6d3906371e7535fa21764de8764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Sp=C3=A4th?= <julian.spaeth@wzw.tum.de> Date: Tue, 7 Apr 2020 16:37:42 +0200 Subject: [PATCH] Show drug info --- src/app/analysis.service.ts | 18 ++--- .../analysis-window.component.ts | 79 +++++++++++++------ src/app/interfaces.ts | 10 ++- .../explorer-page.component.html | 54 +++++++++---- .../explorer-page/explorer-page.component.ts | 60 +++++++------- src/styles.scss | 4 + 6 files changed, 141 insertions(+), 84 deletions(-) diff --git a/src/app/analysis.service.ts b/src/app/analysis.service.ts index d85b8c67..e33eaae6 100644 --- a/src/app/analysis.service.ts +++ b/src/app/analysis.service.ts @@ -106,18 +106,18 @@ export class AnalysisService { showToast(task: Task, status: 'DONE' | 'FAILED') { let toastMessage; let toastType; - const startDate = new Date(task.info.startedAt); - const finishedDate = new Date(task.info.finishedAt); + // const startDate = new Date(task.info.startedAt); + // const finishedDate = new Date(task.info.finishedAt); if (status === 'DONE') { - toastMessage = `Computation finished succesfully. - \n- Algorithm: ${task.info.algorithm} - \n- Started At: ${startDate.getHours()}:${startDate.getMinutes()} - \n- Finished At: ${finishedDate.getHours()}:${finishedDate.getMinutes()}`; + toastMessage = 'Computation finished succesfully.'; + // \n- Algorithm: ${task.info.algorithm} + // \n- Started At: ${startDate.getHours()}:${startDate.getMinutes()} + // \n- Finished At: ${finishedDate.getHours()}:${finishedDate.getMinutes()}`; toastType = 'is-success'; } else if (status === 'FAILED') { - toastMessage = `Computation failed. - \n- Algorithm: ${task.info.algorithm} - \n- Started At: ${startDate.getHours()}:${startDate.getMinutes()}`; + toastMessage = 'Computation failed.'; + // \n- Algorithm: ${task.info.algorithm} + // \n- Started At: ${startDate.getHours()}:${startDate.getMinutes()}`; toastType = 'is-danger'; } diff --git a/src/app/components/analysis-window/analysis-window.component.ts b/src/app/components/analysis-window/analysis-window.component.ts index ccc612ef..ac64064d 100644 --- a/src/app/components/analysis-window/analysis-window.component.ts +++ b/src/app/components/analysis-window/analysis-window.component.ts @@ -12,7 +12,7 @@ import { import {HttpClient} from '@angular/common/http'; import {environment} from '../../../environments/environment'; import {AnalysisService} from '../../analysis.service'; -import {Protein, Task, NodeType, ViralProtein} from '../../interfaces'; +import {Protein, Task, NodeType, ViralProtein, Drug} from '../../interfaces'; declare var vis: any; @@ -71,23 +71,27 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { const options = {}; this.network = new vis.Network(container, this.nodeData, options); + this.network.on('deselectNode', (properties) => { + this.showDetailsChange.emit([false, [null, null, null, null, null, null]]); + }); this.network.on('selectNode', (properties) => { const selectedNodes = this.nodeData.nodes.get(properties.nodes); if (selectedNodes.length > 0) { - let selectedProteinItem; - let selectedProteinName; - let selectedProteinType; - let selectedProteinAc; - let selectedProteinDataset; - let selectedProteinVirus; + let selectedItem; + let selectedName; + let selectedType; + let selectedId; + let selectedVirusName; + let selectedStatus; if (selectedNodes[0].nodeType === 'host') { const protein: Protein = {name: '', proteinAc: selectedNodes[0].id}; - selectedProteinName = null; - selectedProteinDataset = null; - selectedProteinVirus = null; - selectedProteinItem = {name: selectedNodes[0].id, type: 'Host Protein', data: protein}; - selectedProteinAc = protein.proteinAc; - selectedProteinType = 'Host Protein'; + selectedVirusName = null; + selectedStatus = null; + selectedItem = {name: selectedNodes[0].id, type: 'Host Protein', data: protein}; + // TODO use gene name here + selectedName = protein.proteinAc; + selectedId = protein.proteinAc; + selectedType = 'Host Protein'; if (properties.event.srcEvent.ctrlKey) { if (this.analysis.inSelection(protein.proteinAc)) { this.analysis.removeItem(protein.proteinAc); @@ -97,27 +101,45 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { } } } else if (selectedNodes[0].nodeType === 'virus') { - const virus: ViralProtein = {viralProteinId: null, effectName: selectedNodes[0].id, virusName: null, datasetName: null}; - selectedProteinAc = null; - selectedProteinDataset = null; - selectedProteinVirus = null; - selectedProteinItem = {name: virus.effectName, type: 'Viral Protein', data: virus}; - selectedProteinName = virus.effectName; - selectedProteinType = 'Viral Protein'; + const virus: ViralProtein = { + viralProteinId: null, + effectName: selectedNodes[0].id, + virusName: null, + datasetName: null + }; + selectedId = null; + selectedStatus = null; + selectedItem = {name: virus.effectName, type: 'Viral Protein', data: virus}; + selectedVirusName = virus.virusName; + selectedName = virus.effectName; + selectedType = 'Viral Protein'; if (properties.event.srcEvent.ctrlKey) { if (this.analysis.inSelection(virus.effectName)) { this.analysis.removeItem(virus.effectName); } else { - this.analysis.addItem(selectedProteinItem); + this.analysis.addItem(selectedItem); this.analysis.getCount(); } } + } else if (selectedNodes[0].nodeType === 'drug') { + const drug: Drug = { + drugId: selectedNodes[0].details.drugId, + name: selectedNodes[0].details.name, + status: selectedNodes[0].details.status, + }; + selectedId = drug.drugId; + selectedStatus = drug.status; + selectedName = drug.name; + selectedType = 'Drug'; + selectedItem = {name: drug.name, type: 'Drug', data: drug}; + selectedVirusName = null; } - this.showDetailsChange.emit([true, [selectedProteinItem, selectedProteinName, selectedProteinType, selectedProteinAc, - selectedProteinDataset, selectedProteinVirus]]); + this.showDetailsChange.emit([true, [selectedItem, selectedName, selectedType, + selectedId, selectedVirusName, selectedStatus]]); } else { this.showDetailsChange.emit([false, [null, null, null, null, null, null]]); } + }); this.analysis.subscribe((item, selected) => { @@ -157,6 +179,8 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { public inferNodeType(nodeId: string): 'host' | 'virus' | 'drug' { if (nodeId.indexOf('-') !== -1 || nodeId.indexOf('_') !== -1) { return 'virus'; + } else if (nodeId.startsWith('DB')) { + return 'drug'; } return 'host'; } @@ -174,9 +198,12 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { const nodeTypes = attributes.nodeTypes || {}; const isSeed = attributes.isSeed || {}; const scores = attributes.scores || {}; + const details = attributes.details || {}; + + for (const node of network.nodes) { - nodes.push(this.mapNode(node, nodeTypes[node] || this.inferNodeType(node), isSeed[node], scores[node])); + nodes.push(this.mapNode(node, nodeTypes[node] || this.inferNodeType(node), isSeed[node], scores[node], details[node])); } for (const edge of network.edges) { @@ -225,13 +252,13 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { return {color, shape, size, font, shadow}; } - private mapNode(nodeId: any, nodeType?: NodeType, isSeed?: boolean, score?: number): any { + private mapNode(nodeId: any, nodeType?: NodeType, isSeed?: boolean, score?: number, details?): any { const {shape, color, size, font, shadow} = this.getNodeLooks(nodeId, nodeType, isSeed); return { id: nodeId, label: nodeId, size, color, shape, font, shadow, - nodeType, isSeed, + nodeType, isSeed, details }; } diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts index 5ae4c54a..fb986b18 100644 --- a/src/app/interfaces.ts +++ b/src/app/interfaces.ts @@ -53,6 +53,12 @@ export interface Task { export interface QueryItem { name: string; - type: 'Host Protein' | 'Viral Protein'; - data: Protein | ViralProtein; + type: 'Host Protein' | 'Viral Protein' | 'Drug'; + data: Protein | ViralProtein | Drug; +} + +export interface Drug { + drugId: string; + name: string; + status: 'approved' | 'investigational'; } diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html index 99f89360..8715dc56 100644 --- a/src/app/pages/explorer-page/explorer-page.component.html +++ b/src/app/pages/explorer-page/explorer-page.component.html @@ -148,8 +148,8 @@ <div class="network center image1" #network> <button class="button is-loading center" alt="Snow">Loading</button> </div> - <div class="image2" > - <img src="assets/legend.png" width="160px"/> + <div class="image2"> + <img src="assets/legend.png" width="160px"/> </div> </div> </div> @@ -194,8 +194,11 @@ <header class="card-header"> <p class="card-header-title"> <span class="icon"> - <i class="fas fa-info" aria-hidden="true"></i> - </span> {{ selectedProteinType }} + <i *ngIf="!showDetails" class="fas fa-info" aria-hidden="true"></i> + <i *ngIf="selectedType === 'Host Protein'" class="fas fa-dna" aria-hidden="true"></i> + <i *ngIf="selectedType === 'Viral Protein'" class="fas fa-virus" aria-hidden="true"></i> + <i *ngIf="selectedType === 'Drug'" class="fas fa-capsules" aria-hidden="true"></i> + </span> {{ selectedType }} </p> <a (click)="collabsDetails = !collabsDetails" data-action="collapse" class="card-header-icon is-hidden-fullscreen" aria-label="more options"> @@ -207,27 +210,44 @@ <div *ngIf="collabsDetails"> <div class="card-content"> <div *ngIf="showDetails"> - <p *ngIf="selectedProteinName"><b>Name:</b> {{ selectedProteinName }}</p> - <p *ngIf="selectedProteinDataset"><b>Virus:</b> {{ selectedProteinVirus }}</p> - <p *ngIf="selectedProteinAc"><b>Protein AC:</b> - <a href="https://www.uniprot.org/uniprot/{{ selectedProteinAc }}" - target="_blank"> {{ selectedProteinAc }}</a> + <p *ngIf="selectedName && selectedType=='Drug'"><b><span>Name:</span></b> {{ selectedName }} <span + class="icon is-small"> + <i class="fas fa-capsules"></i> + </span></p> + <p *ngIf="selectedName && selectedType=='Host Protein'"><b><span>Gene Name:</span></b> {{ selectedName }} + <span class="icon is-small"> + <i class="fas fa-dna"></i> + </span></p> + <p *ngIf="selectedVirusName"><b><span>Virus:</span></b> {{ selectedVirusName }} <span class="icon is-small"> + <i class="fas fa-virus"></i> + </span></p> + <p *ngIf="selectedName && selectedType=='Viral Protein'"><b>Effect:</b> {{ selectedName }}</p> + <p *ngIf="selectedId && selectedType == 'Host Protein'"><b>Uniprot AC:</b> + <a href="https://www.uniprot.org/uniprot/{{ selectedId }}" + target="_blank"> {{ selectedId }}</a> + </p> + <p *ngIf="selectedId && selectedType == 'Drug'"><b>DrugBank ID:</b> + <a href="https://www.drugbank.ca/drugs/{{ selectedId }}" + target="_blank"> {{ selectedId }}</a> + </p> + <p *ngIf="selectedStatus === 'investigational' "><b>Status:</b> Investigational <span class="icon is-small"><i class="fas fa-search investigational"></i></span> + <p *ngIf="selectedStatus === 'approved' "><b>Status:</b> Approved <span class="icon is-small"><i class="fas fa-check"></i></span> </p> <div class="field has-addons add-remove-toggle"> - <p class="control"> - <button class="button is-rounded" [class.is-success]="!analysis.inSelection(selectedProteinName)" - [disabled]="analysis.inSelection(selectedProteinName)" - (click)="analysis.addItem(selectedProteinItem)"> + <p *ngIf="selectedType !== 'Drug'" class="control"> + <button class="button is-rounded" [class.is-success]="!analysis.inSelection(selectedName)" + [disabled]="analysis.inSelection(selectedName)" + (click)="analysis.addItem(selectedItem)"> <span class="icon is-small"> <i class="fas fa-plus"></i> </span> <span>Add to Analysis</span> </button> </p> - <p class="control"> - <button class="button is-rounded" [class.is-danger]="analysis.inSelection(selectedProteinName)" - [disabled]="!analysis.inSelection(selectedProteinName)" - (click)="analysis.removeItem(selectedProteinName)"> + <p *ngIf="selectedType !== 'Drug'" class="control"> + <button class="button is-rounded" [class.is-danger]="analysis.inSelection(selectedName)" + [disabled]="!analysis.inSelection(selectedName)" + (click)="analysis.removeItem(selectedName)"> <span>Remove</span> <span class="icon is-small"> <i class="fas fa-trash"></i> diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts index 9d2b19f4..7edd4fc7 100644 --- a/src/app/pages/explorer-page/explorer-page.component.ts +++ b/src/app/pages/explorer-page/explorer-page.component.ts @@ -23,12 +23,12 @@ declare var vis: any; export class ExplorerPageComponent implements OnInit, AfterViewInit { public showDetails = false; - public selectedProteinName = null; - public selectedProteinType = null; - public selectedProteinAc = null; - public selectedProteinItem = null; - public selectedProteinVirus = null; - public selectedProteinDataset = null; + public selectedName = null; + public selectedType = null; + public selectedId = null; + public selectedItem = null; + public selectedVirusName = null; + public selectedStatus = null; public collabsAnalysis = true; public collabsDetails = true; public collabsTask = true; @@ -159,29 +159,29 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { } public changeInfo(showList: any[]) { - this.selectedProteinItem = showList[0]; - this.selectedProteinName = showList[1]; - this.selectedProteinType = showList[2]; - this.selectedProteinAc = showList[3]; - this.selectedProteinDataset = showList[4]; - this.selectedProteinVirus = showList[5]; + this.selectedItem = showList[0]; + this.selectedName = showList[1]; + this.selectedType = showList[2]; + this.selectedId = showList[3]; + this.selectedVirusName = showList[4]; + this.selectedStatus = showList[5]; } public async openSummary(item: QueryItem, zoom: boolean) { - this.selectedProteinAc = null; - this.selectedProteinItem = item; - this.selectedProteinType = item.type; - this.selectedProteinName = item.name; - if (this.selectedProteinType === 'Host Protein') { + this.selectedId = null; + this.selectedItem = item; + this.selectedType = item.type; + this.selectedName = item.name; + if (this.selectedType === 'Host Protein') { const hostProtein = item.data as Protein; - this.selectedProteinAc = hostProtein.proteinAc; + this.selectedId = hostProtein.proteinAc; if (zoom) { this.zoomToNode(`p_${item.name}`); } } else if (item.type === 'Viral Protein') { const viralProtein = item.data as ViralProtein; - this.selectedProteinVirus = viralProtein.virusName; - this.selectedProteinDataset = viralProtein.datasetName; + this.selectedName = viralProtein.effectName; + this.selectedVirusName = viralProtein.virusName; if (zoom) { this.zoomToNode(`eff_${viralProtein.effectName}_${viralProtein.datasetName}_${viralProtein.virusName}`); } @@ -190,12 +190,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { } public async closeSummary() { - this.selectedProteinItem = null; - this.selectedProteinName = null; - this.selectedProteinType = null; - this.selectedProteinAc = null; - this.selectedProteinVirus = null; - this.selectedProteinDataset = null; + this.selectedItem = null; + this.selectedName = null; + this.selectedType = null; + this.selectedId = null; + this.selectedVirusName = null; + this.selectedStatus = null; this.showDetails = false; } @@ -292,14 +292,14 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.network.stabilize(); } - if (this.selectedProteinItem) { - this.zoomToNode(`p_${this.selectedProteinItem.name}`); + if (this.selectedItem) { + this.zoomToNode(`p_${this.selectedItem.name}`); } this.queryItems = []; this.fillQueryItems(this.proteins, this.effects); - if (this.selectedProteinItem) { - this.network.selectNodes(['p_' + this.selectedProteinItem.name]); + if (this.selectedItem) { + this.network.selectNodes(['p_' + this.selectedItem.name]); } } diff --git a/src/styles.scss b/src/styles.scss index 642097fe..041cd42d 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -159,6 +159,10 @@ div.field.has-addons.add-remove-toggle { color: #48C774; } +.fa-search.investigational { + color: #e2b600; +} + .fa-spinner { color: #e2b600; } -- GitLab