diff --git a/src/app/components/analysis-window/analysis-window.component.html b/src/app/components/analysis-window/analysis-window.component.html index 1d690bd6b4622ef841f338cb74af123e3a0c6726..9d3ee34a8caa080a1c9a4f7bcf79127d8bccaaf0 100644 --- a/src/app/components/analysis-window/analysis-window.component.html +++ b/src/app/components/analysis-window/analysis-window.component.html @@ -101,58 +101,101 @@ </footer> </div> <div class="content tab-content scrollable" *ngIf="task && task.info.done" [class.is-visible]="tab === 'table'"> - <h4 class="is-4" *ngIf="tableDrugs.length > 0">Drugs</h4> + <div class="field has-addons footer-toggle-buttons" *ngIf="tableHasScores"> + <p class="control"> + <button class="button is-rounded" [class.is-primary]="tableNormalize" (click)="toggleNormalization(true)"> + <span class="icon is-small"> + <i class="fa fa-ruler-vertical"></i> + </span> + <span>Normalization On</span> + </button> + </p> + <p class="control"> + <button class="button is-rounded" [class.is-primary]="!tableNormalize" (click)="toggleNormalization(false)"> + <span>Off</span> + </button> + </p> + </div> + + <div *ngIf="tableDrugs.length > 0"> + <h4 class="is-4"> + <span class="icon"><i class="fa fa-capsules"></i></span> + <span>Drugs</span> + </h4> + <a [href]="downloadLink('drugs')" class="button is-primary is-outlined is-pulled-right is-small"> + <span class="icon"><i class="fa fa-download"></i></span> + <span>Download</span> + </a> + </div> <table class="table is-striped" *ngIf="tableDrugs.length > 0"> <thead> <tr> <td>ID</td> <td>Name</td> <td>Status</td> - <td>Score</td> + <td *ngIf="tableHasScores">Score</td> </tr> </thead> <tbody> <tr *ngFor="let e of tableDrugs"> - <td>{{e.drugId}}</td> + <td><a href="https://www.drugbank.ca/drugs/{{ e.drugId }}" target="_blank">{{ e.drugId }}</a></td> <td>{{e.name}}</td> <td>{{e.status}}</td> - <td>{{e.score}}</td> + <td *ngIf="tableHasScores">{{e.score}}</td> </tr> </tbody> </table> - <h4 class="is-4" *ngIf="tableProteins.length > 0">Proteins</h4> + <div *ngIf="tableProteins.length > 0"> + <h4 class="is-4"> + <span class="icon"><i class="fa fa-dna"></i></span> + <span>Proteins</span> + </h4> + <a [href]="downloadLink('proteins')" class="button is-primary is-outlined is-pulled-right is-small"> + <span class="icon"><i class="fa fa-download"></i></span> + <span>Download</span> + </a> + </div> <table class="table is-striped" *ngIf="tableProteins.length > 0"> <thead> <tr> <td>AC</td> <td>Gene</td> - <td>Score</td> + <td *ngIf="tableHasScores">Score</td> </tr> </thead> <tbody> <tr *ngFor="let e of tableProteins"> - <td>{{e.proteinAc}}</td> + <td><a href="https://www.uniprot.org/uniprot/{{ e.proteinAc }}" target="_blank">{{ e.proteinAc }}</a></td> <td>{{e.name}}</td> - <td>{{e.score}}</td> + <td *ngIf="tableHasScores">{{e.score}}</td> </tr> </tbody> </table> - <h4 class="is-4" *ngIf="tableViralProteins.length > 0">Viral Proteins</h4> + <div *ngIf="tableViralProteins.length > 0"> + <h4 class="is-4"> + <span class="icon"><i class="fa fa-virus"></i></span> + <span>Viral Proteins</span> + </h4> + <a [href]="downloadLink('viral_proteins')" class="button is-primary is-outlined is-pulled-right is-small"> + <span class="icon"><i class="fa fa-download"></i></span> + <span>Download</span> + </a> + </div> <table class="table is-striped" *ngIf="tableViralProteins.length > 0"> <thead> <tr> <td>Name</td> <td>Virus Strain</td> - <td>Score</td> + <td *ngIf="tableHasScores">Score</td> </tr> </thead> <tbody> <tr *ngFor="let e of tableViralProteins"> <td>{{e.effectName}}</td> <td>{{e.virusName}}</td> - <td>{{e.score}}</td> + <td *ngIf="tableHasScores">{{e.score}}</td> </tr> </tbody> </table> diff --git a/src/app/components/analysis-window/analysis-window.component.scss b/src/app/components/analysis-window/analysis-window.component.scss index aed06d97eb25dc2aba5c18bd4c7e5d39f376572e..7be4c7dd9cb8ffddb358ae13b9b661e79682ce36 100644 --- a/src/app/components/analysis-window/analysis-window.component.scss +++ b/src/app/components/analysis-window/analysis-window.component.scss @@ -20,5 +20,10 @@ div.network { &.scrollable { overflow-y: auto; + padding-right: 10px; + } + + h4 { + margin-top: 60px; } } diff --git a/src/app/components/analysis-window/analysis-window.component.ts b/src/app/components/analysis-window/analysis-window.component.ts index 433fe614c32f033c764ae19915398adc0a80564f..bad571f4a4da4a86eb09aed24e551ce1dc5a6199 100644 --- a/src/app/components/analysis-window/analysis-window.component.ts +++ b/src/app/components/analysis-window/analysis-window.component.ts @@ -17,6 +17,11 @@ import html2canvas from 'html2canvas'; declare var vis: any; +interface Scored { + score: number; // Normalized or unnormalized (whichever user selects, will be displayed in the table) + rawScore: number; // Unnormalized (kept to restore unnormalized value) +} + @Component({ selector: 'app-analysis-window', templateUrl: './analysis-window.component.html', @@ -42,9 +47,11 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { public showDrugs = false; public tab = 'network'; - public tableDrugs: Array<any> = []; - public tableProteins: Array<any> = []; - public tableViralProteins: Array<any> = []; + public tableDrugs: Array<Drug & Scored> = []; + public tableProteins: Array<Protein & Scored> = []; + public tableViralProteins: Array<ViralProtein & Scored> = []; + public tableNormalize = false; + public tableHasScores = false; constructor(private http: HttpClient, public analysis: AnalysisService) { } @@ -74,45 +81,38 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { this.nodeData.nodes = new vis.DataSet(nodes); this.nodeData.edges = new vis.DataSet(edges); - // Fill tables - result.networks.forEach((network, i) => { - const attributes = result.nodeAttributes[i]; - const nodeTypes = attributes.nodeTypes || {}; - const nodeDetails = attributes.details || {}; - const nodeScores = attributes.scores || {}; - const normalizeScore = network.maxScore || 1.0; - network.nodes.forEach((nodeId, j) => { - const nodeType = nodeTypes[nodeId] || this.inferNodeType(nodeId); - const entry = nodeDetails[nodeId] || {}; - entry.score = nodeScores[nodeId] || null; - if (entry.score) { - entry.score /= normalizeScore; - } - switch (nodeType) { - case 'drug': - this.tableDrugs.push(entry); - break; - case 'host': - this.tableProteins.push(entry); - break; - case 'virus': - this.tableViralProteins.push(entry); - break; - } - }); - }); - - this.tableDrugs = this.tableDrugs.reverse(); - this.tableProteins = this.tableProteins.reverse(); - this.tableViralProteins = this.tableViralProteins.reverse(); - const container = this.networkEl.nativeElement; const options = {}; this.network = new vis.Network(container, this.nodeData, options); + + const promises: Promise<any>[] = []; + promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=proteins`).toPromise() + .then((table) => { + this.tableProteins = table; + this.tableProteins.forEach((r) => r.rawScore = r.score); + })); + promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=viral_proteins`).toPromise() + .then((table) => { + this.tableViralProteins = table; + this.tableViralProteins.forEach((r) => r.rawScore = r.score); + })); + promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=drugs`).toPromise() + .then((table) => { + this.tableDrugs = table; + this.tableDrugs.forEach((r) => r.rawScore = r.score); + })); + await Promise.all(promises); + + this.tableHasScores = this.task.info.algorithm === 'trustrank'; + if (this.tableHasScores) { + this.toggleNormalization(true); + } + 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) { @@ -169,7 +169,6 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { } else { this.showDetailsChange.emit([false, [null, null, null, null, null, null]]); } - }); this.analysis.subscribe((item, selected) => { @@ -206,6 +205,42 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { } + public toggleNormalization(normalize: boolean) { + this.tableNormalize = normalize; + + const normalizeFn = (table) => { + let max = 0; + table.forEach(i => { + if (i.rawScore > max) { + max = i.rawScore; + } + }); + table.forEach(i => { + i.score = i.rawScore / max; + }); + }; + + const unnormalizeFn = (table) => { + table.forEach(i => { + i.score = i.rawScore; + }); + }; + + if (normalize) { + normalizeFn(this.tableDrugs); + normalizeFn(this.tableProteins); + normalizeFn(this.tableViralProteins); + } else { + unnormalizeFn(this.tableDrugs); + unnormalizeFn(this.tableProteins); + unnormalizeFn(this.tableViralProteins); + } + } + + public downloadLink(view: string): string { + return `${environment.backend}task_result/?token=${this.token}&view=${view}&fmt=csv`; + } + public inferNodeType(nodeId: string): 'host' | 'virus' | 'drug' { if (nodeId.indexOf('-') !== -1 || nodeId.indexOf('_') !== -1) { return 'virus'; diff --git a/src/app/components/launch-analysis/launch-analysis.component.ts b/src/app/components/launch-analysis/launch-analysis.component.ts index 3d0c929c70af1e9f41238e539f17e437b1697b43..c14a340ed240910b4ee5fe8535f33a9dae10a87a 100644 --- a/src/app/components/launch-analysis/launch-analysis.component.ts +++ b/src/app/components/launch-analysis/launch-analysis.component.ts @@ -2,7 +2,7 @@ import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges import {AnalysisService} from '../../analysis.service'; interface Algorithm { - slug: string; + slug: 'trustrank' | 'keypathwayminer' | 'multisteiner'; name: string; } @@ -51,10 +51,12 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { if (this.target === 'drug-target') { - this.algorithms = [TRUSTRANK, MULTISTEINER, KEYPATHWAYMINER]; + this.algorithms = [MULTISTEINER, TRUSTRANK, KEYPATHWAYMINER]; + this.algorithm = MULTISTEINER.slug; this.trustrankStrain = 'SARS_CoV2'; } else if (this.target === 'drug') { this.algorithms = [TRUSTRANK]; + this.algorithm = TRUSTRANK.slug; this.trustrankStrain = 'drugs'; } }