Skip to content
Snippets Groups Projects
Commit 87835e3a authored by Julian Späth's avatar Julian Späth
Browse files

Merge branch 'final-fixes-and-features' into 'master'

Final fixes and features

See merge request covid-19/frontend!88
parents 2c24ca88 e44177e1
No related branches found
No related tags found
No related merge requests found
import {Wrapper, Task, getWrapperFromProtein, getWrapperFromViralProtein} from './interfaces'; import {Wrapper, Task, getWrapperFromProtein, getWrapperFromViralProtein, Protein, ViralProtein} from './interfaces';
import {Subject} from 'rxjs'; import {Subject} from 'rxjs';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {environment} from '../environments/environment'; import {environment} from '../environments/environment';
...@@ -121,6 +121,14 @@ export class AnalysisService { ...@@ -121,6 +121,14 @@ export class AnalysisService {
return this.selectedItems.has(wrapper.nodeId); return this.selectedItems.has(wrapper.nodeId);
} }
proteinInSelection(protein: Protein): boolean {
return this.inSelection(getWrapperFromProtein(protein));
}
viralProteinInSelection(viralProtein: ViralProtein): boolean {
return this.inSelection(getWrapperFromViralProtein(viralProtein));
}
removeItem(wrapper: Wrapper) { removeItem(wrapper: Wrapper) {
const item = this.selectedItems.get(wrapper.nodeId); const item = this.selectedItems.get(wrapper.nodeId);
if (this.selectedItems.delete(wrapper.nodeId)) { if (this.selectedItems.delete(wrapper.nodeId)) {
......
...@@ -48,8 +48,7 @@ import {AnalysisService} from './analysis.service'; ...@@ -48,8 +48,7 @@ import {AnalysisService} from './analysis.service';
TableModule, TableModule,
], ],
providers: [AnalysisService], providers: [AnalysisService],
bootstrap: [AppComponent] bootstrap: [AppComponent],
}) })
export class AppModule { export class AppModule {
} }
...@@ -74,48 +74,40 @@ ...@@ -74,48 +74,40 @@
</div> </div>
</div> </div>
<footer class="card-footer toolbar"> <footer class="card-footer toolbar">
<div class="field has-addons"> <div class="field">
<p class="control"> <p class="control footer-buttons">
<button class="button is-rounded is-success is-rounded" [disabled]="true"> <button class="button is-primary is-rounded" (click)="toCanvas()">
<span class="icon"> <span class="icon">
<i class="fas fa-cloud-download-alt" aria-hidden="true"></i> <i class="fas fa-camera" aria-hidden="true"></i>
</span> </span>
<span>
Export Results
</span>
</button>
</p>
<p class="control">
<button class="button is-primary is-rounded" [disabled]="true">
<span class="icon">
<i class="fas fa-camera" aria-hidden="true"></i>
</span>
<span> <span>
Screenshot Screenshot
</span> </span>
</button> </button>
</p> </p>
<p class="control"> </div>
<div class="field">
<p class="control footer-buttons">
<button class="button is-danger is-rounded" (click)="analysis.removeTask(token); close()"> <button class="button is-danger is-rounded" (click)="analysis.removeTask(token); close()">
<span class="icon"> <span class="icon">
<i class="fas fa-trash" aria-hidden="true"></i> <i class="fas fa-trash" aria-hidden="true"></i>
</span> </span>
<span> <span>
Delete Analysis Delete Analysis
</span> </span>
</button> </button>
</p> </p>
</div> </div>
<app-toggle *ngIf="task.info.target === 'drug-target'" class="footer-toggle-buttons" textOn="Drugs On" textOff="Off" <app-toggle *ngIf="task.info.target === 'drug-target'" class="footer-buttons" textOn="Drugs On" textOff="Off"
[value]="showDrugs" (valueChange)="toggleDrugs($event)" icon="fa-capsules"></app-toggle> [value]="showDrugs" (valueChange)="toggleDrugs($event)" icon="fa-capsules"></app-toggle>
<app-toggle class="footer-toggle-buttons" textOn="Animation On" textOff="Off" <app-toggle class="footer-buttons" textOn="Animation On" textOff="Off"
[value]="physicsEnabled" (valueChange)="updatePhysicsEnabled($event)" icon="fa-wind"></app-toggle> [value]="physicsEnabled" (valueChange)="updatePhysicsEnabled($event)" icon="fa-wind"></app-toggle>
</footer> </footer>
</div> </div>
<div class="content tab-content scrollable" *ngIf="task && task.info.done" [class.is-visible]="tab === 'table'"> <div class="content tab-content scrollable" *ngIf="task && task.info.done" [class.is-visible]="tab === 'table'">
<div class="field has-addons footer-toggle-buttons" *ngIf="tableHasScores"> <div class="field has-addons" *ngIf="tableHasScores">
<p class="control"> <p class="control">
<button class="button is-rounded" [class.is-primary]="tableNormalize" (click)="toggleNormalization(true)"> <button class="button is-rounded" [class.is-primary]="tableNormalize" (click)="toggleNormalization(true)">
<span class="icon is-small"> <span class="icon is-small">
...@@ -167,7 +159,7 @@ ...@@ -167,7 +159,7 @@
<td><a href="https://www.drugbank.ca/drugs/{{ e.drugId }}" target="_blank">{{ e.drugId }}</a></td> <td><a href="https://www.drugbank.ca/drugs/{{ e.drugId }}" target="_blank">{{ e.drugId }}</a></td>
<td>{{e.name}}</td> <td>{{e.name}}</td>
<td>{{e.status}}</td> <td>{{e.status}}</td>
<td *ngIf="tableHasScores">{{e.score || ''}}</td> <td *ngIf="tableHasScores">{{e.score ? (e.score | number) : ''}}</td>
</tr> </tr>
</ng-template> </ng-template>
</p-table> </p-table>
...@@ -182,11 +174,16 @@ ...@@ -182,11 +174,16 @@
<span>Download</span> <span>Download</span>
</a> </a>
</div> </div>
<p-table *ngIf="tableProteins.length > 0" [value]="tableProteins"> <p-table *ngIf="tableProteins.length > 0" selectionMode="multiple"
[value]="tableProteins" [selection]="tableSelectedProteins" dataKey="proteinAc"
(selectionChange)="tableProteinSelection($event)">
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th class="checkbox-col">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th [pSortableColumn]="'proteinAc'"> <th [pSortableColumn]="'proteinAc'">
AC UniProt Code
<p-sortIcon [field]="'proteinAc'"></p-sortIcon> <p-sortIcon [field]="'proteinAc'"></p-sortIcon>
</th> </th>
<th [pSortableColumn]="'name'"> <th [pSortableColumn]="'name'">
...@@ -201,9 +198,12 @@ ...@@ -201,9 +198,12 @@
</ng-template> </ng-template>
<ng-template pTemplate="body" let-e> <ng-template pTemplate="body" let-e>
<tr> <tr>
<td>
<p-tableCheckbox [value]="e"></p-tableCheckbox>
</td>
<td><a href="https://www.uniprot.org/uniprot/{{ e.proteinAc }}" target="_blank">{{ e.proteinAc }}</a></td> <td><a href="https://www.uniprot.org/uniprot/{{ e.proteinAc }}" target="_blank">{{ e.proteinAc }}</a></td>
<td>{{e.name}}</td> <td>{{e.name}}</td>
<td *ngIf="tableHasScores">{{e.score || ''}}</td> <td *ngIf="tableHasScores">{{e.score ? (e.score | number) : ''}}</td>
</tr> </tr>
</ng-template> </ng-template>
</p-table> </p-table>
...@@ -218,28 +218,36 @@ ...@@ -218,28 +218,36 @@
<span>Download</span> <span>Download</span>
</a> </a>
</div> </div>
<p-table *ngIf="tableViralProteins.length > 0" [value]="tableViralProteins"> <p-table *ngIf="tableViralProteins.length > 0" selectionMode="multiple"
[value]="tableViralProteins" [selection]="tableSelectedViralProteins" dataKey="effectId"
(selectionChange)="tableViralProteinSelection($event)">
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<td [pSortableColumn]="'effectName'"> <th class="checkbox-col">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th [pSortableColumn]="'effectName'">
Name Name
<p-sortIcon [field]="'effectName'"></p-sortIcon> <p-sortIcon [field]="'effectName'"></p-sortIcon>
</td> </th>
<td [pSortableColumn]="'virusName'"> <th [pSortableColumn]="'virusName'">
Virus Strain Virus Strain
<p-sortIcon [field]="'virusName'"></p-sortIcon> <p-sortIcon [field]="'virusName'"></p-sortIcon>
</td> </th>
<td *ngIf="tableHasScores" [pSortableColumn]="'score'"> <th *ngIf="tableHasScores" [pSortableColumn]="'score'">
Score Score
<p-sortIcon [field]="'score'"></p-sortIcon> <p-sortIcon [field]="'score'"></p-sortIcon>
</td> </th>
</tr> </tr>
</ng-template> </ng-template>
<ng-template pTemplate="body" let-e> <ng-template pTemplate="body" let-e>
<tr> <tr>
<td>
<p-tableCheckbox [value]="e"></p-tableCheckbox>
</td>
<td>{{e.effectName}}</td> <td>{{e.effectName}}</td>
<td>{{e.virusName}}</td> <td>{{e.virusName}}</td>
<td *ngIf="tableHasScores">{{e.score || ''}}</td> <td *ngIf="tableHasScores">{{e.score ? (e.score | number) : ''}}</td>
</tr> </tr>
</ng-template> </ng-template>
</p-table> </p-table>
......
...@@ -31,3 +31,7 @@ div.network { ...@@ -31,3 +31,7 @@ div.network {
.table-header { .table-header {
margin-bottom: 50px; margin-bottom: 50px;
} }
.checkbox-col {
width: 50px;
}
...@@ -13,8 +13,19 @@ import {HttpClient, HttpErrorResponse} from '@angular/common/http'; ...@@ -13,8 +13,19 @@ import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {environment} from '../../../environments/environment'; import {environment} from '../../../environments/environment';
import {AnalysisService, algorithmNames} from '../../analysis.service'; import {AnalysisService, algorithmNames} from '../../analysis.service';
import { import {
Protein, Task, ViralProtein, Drug, Wrapper, WrapperType, Protein,
getWrapperFromProtein, getWrapperFromDrug, getWrapperFromViralProtein, getNodeIdsFromPDI, getNodeIdsFromPPI Task,
ViralProtein,
Drug,
Wrapper,
WrapperType,
getWrapperFromProtein,
getWrapperFromDrug,
getWrapperFromViralProtein,
getNodeIdsFromPDI,
getNodeIdsFromPPI,
getViralProteinNodeId,
getProteinNodeId
} from '../../interfaces'; } from '../../interfaces';
import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
import {toast} from 'bulma-toast'; import {toast} from 'bulma-toast';
...@@ -59,7 +70,9 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -59,7 +70,9 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
public tableDrugs: Array<Drug & Scored> = []; public tableDrugs: Array<Drug & Scored> = [];
public tableProteins: Array<Protein & Scored> = []; public tableProteins: Array<Protein & Scored> = [];
public tableSelectedProteins: Array<Protein & Scored> = [];
public tableViralProteins: Array<ViralProtein & Scored> = []; public tableViralProteins: Array<ViralProtein & Scored> = [];
public tableSelectedViralProteins: Array<ViralProtein & Scored> = [];
public tableNormalize = false; public tableNormalize = false;
public tableHasScores = false; public tableHasScores = false;
...@@ -98,12 +111,17 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -98,12 +111,17 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
this.network = new vis.Network(container, this.nodeData, options); this.network = new vis.Network(container, this.nodeData, options);
const promises: Promise<any>[] = []; const promises: Promise<any>[] = [];
promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=proteins`).toPromise() promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=proteins`).toPromise()
.then((table) => { .then((table) => {
this.tableProteins = table; this.tableProteins = table;
this.tableProteins.forEach((r) => r.rawScore = r.score); this.tableSelectedProteins = [];
this.tableProteins.forEach((r) => {
r.rawScore = r.score;
if (this.analysis.proteinInSelection(r)) {
this.tableSelectedProteins.push(r);
}
});
})); }));
promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=viral_proteins`).toPromise() promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=viral_proteins`).toPromise()
.then((table) => { .then((table) => {
...@@ -156,8 +174,31 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -156,8 +174,31 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
} }
}); });
this.analysis.subscribe((item, selected) => { this.analysis.subscribe((item, selected) => {
if (item.type === 'host') {
// TODO: Refactor!
const found = this.tableSelectedProteins.findIndex((i) => getProteinNodeId(i) === item.nodeId);
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
this.tableSelectedProteins.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
this.tableSelectedProteins.splice(found, 1);
}
this.tableSelectedProteins = [...this.tableSelectedProteins];
} else if (item.type === 'virus') {
// TODO: Refactor!
const found = this.tableSelectedViralProteins.findIndex((i) => getViralProteinNodeId(i) === item.nodeId);
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
this.tableSelectedViralProteins.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
this.tableSelectedViralProteins.splice(found, 1);
}
this.tableSelectedViralProteins = [...this.tableSelectedViralProteins];
}
const node = this.nodeData.nodes.get(item.nodeId); const node = this.nodeData.nodes.get(item.nodeId);
if (!node) { if (!node) {
return; return;
...@@ -192,10 +233,6 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -192,10 +233,6 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
this.emitVisibleItems(false); this.emitVisibleItems(false);
} }
export() {
}
public toggleNormalization(normalize: boolean) { public toggleNormalization(normalize: boolean) {
this.tableNormalize = normalize; this.tableNormalize = normalize;
...@@ -351,9 +388,9 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -351,9 +388,9 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
this.drugNodes = []; this.drugNodes = [];
this.drugEdges = []; this.drugEdges = [];
if (this.showDrugs) { if (this.showDrugs) {
const proteinAcs = this.proteins.map((protein) => protein.proteinAc); const result = await this.http.get<any>(
// tslint:disable-next-line:max-line-length `${environment.backend}drug_interactions/?token=${this.token}`).toPromise().catch(
const result = await this.http.get<any>(`${environment.backend}drug_interactions/?proteins=${JSON.stringify(proteinAcs)}`).toPromise().catch((err: HttpErrorResponse) => { (err: HttpErrorResponse) => {
// simple logging, but you can do a lot more, see below // simple logging, but you can do a lot more, see below
toast({ toast({
message: 'An error occured while fetching the drugs.', message: 'An error occured while fetching the drugs.',
...@@ -396,7 +433,6 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -396,7 +433,6 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
} }
} }
public updatePhysicsEnabled(bool: boolean) { public updatePhysicsEnabled(bool: boolean) {
this.physicsEnabled = bool; this.physicsEnabled = bool;
this.network.setOptions({ this.network.setOptions({
...@@ -409,21 +445,48 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -409,21 +445,48 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
}); });
} }
public screenshot() { public toCanvas() {
const elem = document.getElementById(this.indexscreenshot.toString()); html2canvas(this.networkEl.nativeElement).then((canvas) => {
html2canvas(elem).then((canvas) => { const generatedImage = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
const generatedImage1 = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
const a = document.createElement('a'); const a = document.createElement('a');
a.href = generatedImage1; a.href = generatedImage;
a.download = `Resulting_Network.png`; a.download = `Network.png`;
a.click(); a.click();
}); });
} }
public updateshowdrugs(bool) { public tableProteinSelection(e) {
this.drugstatus = bool; const oldSelection = [...this.tableSelectedProteins];
this.tableSelectedProteins = e;
for (const i of this.tableSelectedProteins) {
const wrapper = getWrapperFromProtein(i);
if (oldSelection.indexOf(i) === -1) {
this.analysis.addItem(wrapper);
}
}
for (const i of oldSelection) {
const wrapper = getWrapperFromProtein(i);
if (this.tableSelectedProteins.indexOf(i) === -1) {
this.analysis.removeItem(wrapper);
}
}
}
public tableViralProteinSelection(e) {
const oldSelection = [...this.tableSelectedViralProteins];
this.tableSelectedViralProteins = e;
for (const i of this.tableSelectedViralProteins) {
const wrapper = getWrapperFromViralProtein(i);
if (oldSelection.indexOf(i) === -1) {
this.analysis.addItem(wrapper);
}
}
for (const i of oldSelection) {
const wrapper = getWrapperFromViralProtein(i);
if (this.tableSelectedViralProteins.indexOf(i) === -1) {
this.analysis.removeItem(wrapper);
}
}
} }
} }
...@@ -148,7 +148,7 @@ ...@@ -148,7 +148,7 @@
</p> </p>
</header> </header>
<div class="card-content"> <div class="card-content">
<div class="card-image" id="0"> <div class="card-image" id="canvas-content">
<div class="parent"> <div class="parent">
<div class="network center image1" #network> <div class="network center image1" #network>
<button class="button is-loading center" alt="Snow">Loading</button> <button class="button is-loading center" alt="Snow">Loading</button>
...@@ -164,7 +164,7 @@ ...@@ -164,7 +164,7 @@
<i class="fas fa-camera" aria-hidden="true"></i> <i class="fas fa-camera" aria-hidden="true"></i>
</span> <span>Screenshot</span> </span> <span>Screenshot</span>
</button> </button>
<app-toggle class="footer-toggle-buttons" textOn="Animation On" textOff="Off" [value]="physicsEnabled" (valueChange)="updatePhysicsEnabled($event)" icon="fa-wind"></app-toggle> <app-toggle class="footer-buttons" textOn="Animation On" textOff="Off" [value]="physicsEnabled" (valueChange)="updatePhysicsEnabled($event)" icon="fa-wind"></app-toggle>
</footer> </footer>
</div> </div>
</div> </div>
......
...@@ -63,7 +63,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -63,7 +63,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public selectedAnalysisToken: string | null = null; public selectedAnalysisToken: string | null = null;
public currentDataset = []; public currentDataset = [];
private screenshotArray = [0];
public currentViewProteins: Protein[]; public currentViewProteins: Protein[];
public currentViewEffects: ViralProtein[]; public currentViewEffects: ViralProtein[];
...@@ -417,15 +416,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -417,15 +416,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
} }
public toCanvas() { public toCanvas() {
this.screenshotArray.forEach((key, index) => { html2canvas(this.networkEl.nativeElement).then((canvas) => {
const elem = document.getElementById(index.toString()); const generatedImage = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
html2canvas(elem).then((canvas) => { const a = document.createElement('a');
const generatedImage = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream'); a.href = generatedImage;
const a = document.createElement('a'); a.download = `Network.png`;
a.href = generatedImage; a.click();
a.download = `Network.png`;
a.click();
});
}); });
} }
......
...@@ -12,7 +12,7 @@ $danger: #EF476F; ...@@ -12,7 +12,7 @@ $danger: #EF476F;
$link: $primary; $link: $primary;
$info: $primary; $info: $primary;
@import "~bulma/bulma.sass"; @import "~bulma/bulma.sass";
@import "~primeng/resources/primeng.css"; @import "~primeng/resources/primeng.min.css";
@import "~primeicons/primeicons.css"; @import "~primeicons/primeicons.css";
html { html {
...@@ -174,7 +174,7 @@ div.field.has-addons.add-remove-toggle { ...@@ -174,7 +174,7 @@ div.field.has-addons.add-remove-toggle {
color: $danger; color: $danger;
} }
.footer-toggle-buttons { .footer-buttons {
margin-left: 20px; margin-left: 20px;
margin-right: 10px; margin-right: 10px;
} }
...@@ -192,3 +192,8 @@ body { ...@@ -192,3 +192,8 @@ body {
margin: 0; margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif; font-family: Roboto, "Helvetica Neue", sans-serif;
} }
.ui-chkbox-box {
border: 1px solid black !important;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment