Skip to content
Snippets Groups Projects
Commit 9fd06189 authored by Julian Matschinske's avatar Julian Matschinske
Browse files

Analysis window component

parent c474545c
No related branches found
No related tags found
No related merge requests found
Showing
with 409 additions and 214 deletions
......@@ -7,22 +7,30 @@ import {environment} from '../environments/environment';
@Injectable({
providedIn: 'root'
})
export class AnalysisService {
private selectedProteins = new Map<string, Protein>();
private selectSubject = new Subject<{protein: Protein, selected: boolean}>();
private selectSubject = new Subject<{ protein: Protein, selected: boolean }>();
private token: string | null = null;
public tokens: any[] = [];
private stats: any;
private task: any;
public tasks: any[] = [];
private intervalId: any;
constructor(private http: HttpClient) {
this.token = localStorage.getItem('token');
if (this.token) {
this.startWatching();
const tokens = localStorage.getItem('tokens');
if (tokens) {
this.tokens = JSON.parse(tokens);
}
this.startWatching();
}
async getTasks() {
return await this.http.get<any>(`${environment.backend}tasks/?tokens=${JSON.stringify(this.tokens)}`).toPromise().catch((e) => {
clearInterval(this.intervalId);
});
}
addProtein(protein: Protein) {
......@@ -56,13 +64,17 @@ export class AnalysisService {
});
}
getTask(): any {
return this.task;
getTask(token): any {
this.tasks.forEach((task) => {
if (task.token === token) {
return task;
}
});
}
reset() {
this.token = null;
this.task = null;
this.tokens = null;
this.tasks = null;
this.stats = null;
if (this.intervalId) {
clearInterval(this.intervalId);
......@@ -78,20 +90,15 @@ export class AnalysisService {
algorithm,
parameters,
}).toPromise();
this.token = resp.token;
localStorage.setItem('token', this.token);
this.tokens.push(resp.token);
localStorage.setItem('tokens', JSON.stringify(this.tokens));
this.startWatching();
}
async startWatching() {
startWatching() {
this.intervalId = setInterval(async () => {
const resp = await this.http.get<any>(`${environment.backend}task/?token=${this.token}`).toPromise().catch((e) => {
clearInterval(this.intervalId);
});
this.task = resp.task;
this.stats = resp.stats;
if (this.task.done) {
clearInterval(this.intervalId);
if (this.tokens.length > 0) {
this.tasks = await this.getTasks();
}
}, 1000);
}
......
......@@ -16,6 +16,7 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {ProteinAnalysisComponent} from './components/protein-analysis/protein-analysis.component';
import {SelectDatasetComponent} from './components/select-dataset/select-dataset.component';
import {AnalysisWindowComponent} from './components/analysis-window/analysis-window.component';
import { TaskListComponent } from './components/task-list/task-list.component';
@NgModule({
......@@ -28,6 +29,7 @@ import {AnalysisWindowComponent} from './components/analysis-window/analysis-win
ProteinAnalysisComponent,
SelectDatasetComponent,
AnalysisWindowComponent,
TaskListComponent,
],
imports: [
BrowserModule,
......
<div class="card analysis">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-flask" aria-hidden="true"></i>
</span>
Analysis
</p>
<a (click)="closeAnalysisWindow()" class="card-header-icon" aria-label="more options">
<span class="icon">
<i class="fas fa-times" aria-hidden="true"></i>
</span>
</a>
</header>
<div class="card-content">
<div class="content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis mauris.
<a href="#">@bulmaio</a>. <a href="#">#css</a> <a href="#">#responsive</a>
<br>
<time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
<div *ngIf="token">
<div class="card analysis" *ngIf="info && info.done">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-flask" aria-hidden="true"></i>
</span>
Analysis Results
</p>
<a (click)="close()" class="card-header-icon" aria-label="close">
<span class="icon">
<i class="fas fa-times" aria-hidden="true"></i>
</span>
</a>
</header>
<div class="card-content">
<div class="content">
<div class="network center" #network>
<button class="button is-loading center">Loading</button>
</div>
</div>
<button (click)="toggleDrugs()">Show/hide</button>
</div>
<footer class="card-footer">
</footer>
</div>
<div class="card analysis" *ngIf="info && !info.startedAt">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-cog" aria-hidden="true"></i>
</span>
Analysis Queued...
</p>
<a (click)="close()" class="card-header-icon" aria-label="close">
<span class="icon">
<i class="fas fa-times" aria-hidden="true"></i>
</span>
</a>
</header>
<div class="card-content">
<div class="content">
The analysis is queued
<!--TODO: Display queue information-->
</div>
</div>
<footer class="card-footer">
</footer>
</div>
<div class="card analysis" *ngIf="info && info.startedAt && !info.done">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-cog fa-spin" aria-hidden="true"></i>
</span>
Analysis in Progress...
</p>
<a (click)="close()" class="card-header-icon" aria-label="close">
<span class="icon">
<i class="fas fa-times" aria-hidden="true"></i>
</span>
</a>
</header>
<div class="card-content">
<div class="content">
The analysis is in progress
<!--TODO: Display analysis progress-->
</div>
</div>
<footer class="card-footer">
</footer>
</div>
<div class="card analysis" *ngIf="!info">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-question" aria-hidden="true"></i>
</span>
Analysis not found
</p>
<a (click)="close()" class="card-header-icon" aria-label="more options">
<span class="icon">
<i class="fas fa-times" aria-hidden="true"></i>
</span>
</a>
</header>
<div class="card-content">
<div class="content">
<span class="notification is-danger">
The analysis you were looking for is either gone or never existed.
</span>
</div>
</div>
<footer class="card-footer">
</footer>
</div>
<footer class="card-footer">
<button [disabled]="true" (click)="export()" class="card-footer-item button is-primary"><span class="icon">
<i class="fas fa-cloud-download-alt" aria-hidden="true"></i>
</span><span>Export</span></button>
<button [disabled]="true" (click)="discard()" class="card-footer-item button is-danger"><span>Discard</span><span class="icon">
<i class="fas fa-trash" aria-hidden="true"></i>
</span></button>
<button (click)="closeAnalysisWindow()" class="card-footer-item button"><span>Close</span> <span class="icon">
<i class="fas fa-times" aria-hidden="true"></i>
</span></button>
</footer>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import { AnalysisWindowComponent } from './analysis-window.component';
import {AnalysisWindowComponent} from './analysis-window.component';
import {HttpClientModule} from '@angular/common/http';
describe('AnalysisWindowComponent', () => {
let component: AnalysisWindowComponent;
......@@ -8,9 +9,10 @@ describe('AnalysisWindowComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AnalysisWindowComponent ]
declarations: [AnalysisWindowComponent],
imports: [HttpClientModule],
})
.compileComponents();
.compileComponents();
}));
beforeEach(() => {
......
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from '../../../environments/environment';
import {Edge, Effect, getDatasetFilename, Protein, ProteinNetwork} from '../../pages/protein-network';
import {AnalysisService} from '../../analysis.service';
declare var vis: any;
@Component({
selector: 'app-analysis-window',
templateUrl: './analysis-window.component.html',
styleUrls: ['./analysis-window.component.scss']
})
export class AnalysisWindowComponent implements OnInit {
export class AnalysisWindowComponent implements OnInit, OnChanges {
@Input() token: string | null = null;
@Output() tokenChange = new EventEmitter<string | null>();
public info: any = null;
public stats: any = null;
@Input() analysisWindow: boolean;
@Output() closeWindow = new EventEmitter<boolean>();
@ViewChild('network', {static: false}) networkEl: ElementRef;
constructor() { }
private network: any;
private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
private drugNodes = [];
private showDrugs = false;
ngOnInit(): void {
constructor(private http: HttpClient, private analysis: AnalysisService) {
}
closeAnalysisWindow() {
this.closeWindow.emit(this.analysisWindow);
async ngOnInit() {
}
async ngOnChanges(changes: SimpleChanges) {
if (this.token) {
const {info, stats} = await this.getTask(this.token);
this.info = info;
this.stats = stats;
if (this.info && this.info.done) {
const result = await this.http.get<any>(`${environment.backend}result/?token=${this.token}`).toPromise();
this.createNetwork(result);
}
}
}
private async getTask(token: string): Promise<any> {
return await this.http.get(`${environment.backend}task/?token=${token}`).toPromise();
}
close() {
this.token = null;
this.tokenChange.emit(this.token);
}
discard() {
......@@ -26,4 +61,100 @@ export class AnalysisWindowComponent implements OnInit {
export() {
}
public async createNetwork(result: any) {
const {nodes, edges} = this.mapDataToNodes(result);
this.nodeData.nodes = new vis.DataSet(nodes);
this.nodeData.edges = new vis.DataSet(edges);
const container = this.networkEl.nativeElement;
const options = {
layout: {
improvedLayout: false,
},
};
this.network = new vis.Network(container, this.nodeData, options);
}
private mapProteinToNode(protein: any): any {
let color = '#e2b600';
if (this.analysis.inSelection(protein)) {
color = '#c42eff';
}
return {
id: `p_${protein.proteinAc}`,
label: `${protein.proteinAc}`,
size: 10, color, shape: 'ellipse', shadow: false,
};
}
private mapDrugToNode(drug: any): any {
let color = '#ffffff';
if (drug.status === 'investigational') {
color = '#ffa066';
} else if (drug.status === 'approved') {
color = '#a0ff66';
}
return {
id: `d_${drug.drugId}`,
label: `${drug.name}`,
size: 10, color, shape: 'ellipse', shadow: true, font: {color: '#000000', size: 5},
};
}
private mapProteinProteinInteractionToEdge(edge: any): any {
return {
from: `p_${edge.from}`,
to: `p_${edge.to}`,
color: {color: '#afafaf', highlight: '#854141'},
};
}
private mapDrugProteinInteractionToEdge(edge: any): any {
return {
from: `p_${edge.proteinAc}`,
to: `d_${edge.drugId}`,
color: {color: '#afafaf', highlight: '#854141'},
};
}
private mapDataToNodes(result: any): { nodes: any[], edges: any[] } {
const nodes = [];
const edges = [];
for (const protein of result.proteins) {
nodes.push(this.mapProteinToNode(protein));
}
for (const drug of result.drugs) {
this.drugNodes.push(this.mapDrugToNode(drug));
}
for (const network of result.networks) {
for (const edge of network.ppEdges) {
edges.push(this.mapProteinProteinInteractionToEdge(edge));
}
}
for (const edge of result.dpEdges) {
edges.push(this.mapDrugProteinInteractionToEdge(edge));
}
return {
nodes,
edges,
};
}
public toggleDrugs() {
this.showDrugs = !this.showDrugs;
if (!this.showDrugs) {
this.nodeData.nodes.remove(this.drugNodes);
} else {
this.nodeData.nodes.add(this.drugNodes);
}
}
}
<div class="modal" [class.is-active]="show">
<div class="modal-background"></div>
<div class="modal-card" *ngIf="!analysis.getTask()">
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Launch Protein Analysis</p>
<button class="delete" aria-label="close" (click)="close()"></button>
......@@ -35,62 +35,10 @@
<span class="icon"><i class="fa fa-play"></i></span>
<span>Key Pathway Miner</span>
</button>
<button class="button is-primary" (click)="startTask()">
<button class="button is-primary" (click)="startTask(); close()">
<span class="icon"><i class="fa fa-play"></i></span>
<span>Demo</span>
</button>
</footer>
</div>
<div class="modal-card" *ngIf="analysis.getTask()">
<div *ngIf="!analysis.getTask().startedAt">
<header class="modal-card-head">
<p class="modal-card-title">Queued...</p>
<button class="delete" aria-label="close" (click)="close()"></button>
</header>
<section class="modal-card-body">
<p>
Queue position: {{analysis.getStats().queuePosition}}
</p>
<p>
Queue length: {{analysis.getStats().queueLength}}
</p>
</section>
<footer class="modal-card-foot">
<button class="button is-danger" (click)="analysis.reset()">
<span class="icon"><i class="fa fa-stop"></i></span>
<span>Stop</span>
</button>
</footer>
</div>
<div *ngIf="analysis.getTask().startedAt && !analysis.getTask().done">
<header class="modal-card-head">
<p class="modal-card-title">Running...</p>
<button class="delete" aria-label="close" (click)="close()"></button>
</header>
<section class="modal-card-body">
<progress class="progress is-primary" [value]="analysis.getTask().progress * 100" max="100">15%</progress>
</section>
<footer class="modal-card-foot">
<button class="button is-danger" (click)="analysis.reset()">
<span class="icon"><i class="fa fa-stop"></i></span>
<span>Stop</span>
</button>
</footer>
</div>
<div *ngIf="analysis.getTask().done">
<header class="modal-card-head">
<p class="modal-card-title">View Results</p>
<button class="delete" aria-label="close" (click)="close()"></button>
</header>
<section class="modal-card-body">
<pre>{{analysis.getTask().result}}</pre>
</section>
<footer class="modal-card-foot">
<button class="button is-danger" (click)="analysis.reset()">
<span class="icon"><i class="fa fa-stop"></i></span>
<span>Discard</span>
</button>
</footer>
</div>
</div>
</div>
<div class="content">
<div class="list is-hoverable">
<a *ngFor="let task of analysis.tasks" class="list-item">
<div *ngIf="!task.info.startedAt">
<a (click)="open(task.token)"><b>Algorithm: {{task.info.algorithm}}</b></a><br>
Queue Length: {{task.stats.queueLength}}<br>
Queue Position:{{task.stats.queuePosition}}
</div>
<div *ngIf="task.info.startedAt && !task.info.done">
<a (click)="open(task.token)">Algorithm: {{task.info.algorithm}}</a><br>
<progress class="progress is-primary" [value]="task.info.progress * 100" max="100"></progress>
</div>
<div *ngIf="task.info.done">
<a (click)="open(task.token)"><span>Algorithm: {{task.info.algorithm}}</span>
<span class="icon is-success"><i class="fas fa-check is-success"
aria-hidden="true"></i>
</span></a>
</div>
</a>
</div>
</div>
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TaskListComponent} from './task-list.component';
import {HttpClientModule} from '@angular/common/http';
describe('TaskListComponent', () => {
let component: TaskListComponent;
let fixture: ComponentFixture<TaskListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TaskListComponent],
imports: [HttpClientModule],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TaskListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {AnalysisService} from '../../analysis.service';
@Component({
selector: 'app-task-list',
templateUrl: './task-list.component.html',
styleUrls: ['./task-list.component.scss']
})
export class TaskListComponent implements OnInit {
@Output() token: EventEmitter<any> = new EventEmitter();
constructor(public analysis: AnalysisService) {
}
ngOnInit(): void {
}
open(token) {
this.token.emit(token);
}
}
<app-protein-analysis [(show)]="showAnalysisDialog"></app-protein-analysis>
<div class="content explorer">
<div class="covex explorer">
<div class="content left-window">
<div class="covex left-window">
<div>
<div class="content bar-left">
<div class="covex bar-left">
<div class="card bar-small">
<header class="card-header">
......@@ -95,9 +95,7 @@
</div>
</div>
<div class="content network">
<div class="covex network">
<div class="card network">
<header class="card-header">
<p class="card-header-title">
......@@ -127,12 +125,12 @@
</div>
</div>
<div class="analysis-view" [hidden]="!analysisWindow">
<app-analysis-window (closeWindow)="closeAnalysisWindow()"></app-analysis-window>
<div class="analysis-view" *ngIf="selectedAnalysisToken">
<app-analysis-window [(token)]="selectedAnalysisToken"></app-analysis-window>
</div>
</div>
<div class="content bar-right">
<div class="covex bar-right">
<div class="card bar-medium">
<header class="card-header">
......@@ -158,7 +156,7 @@
<div *ngIf="!showDetails">
Please select a node for further information.
<a (click)="showAnalysisWindow()"> Open Analysis Window </a>
<!-- <a (click)="selectedAnalysisToken = 'oy4UsXfBDobTucdQBhN9IUzfnpqKwzqx'"> Open Analysis Window </a>-->
</div>
</div>
......@@ -181,13 +179,9 @@
</button>
<p></p>
<button (click)="showAnalysisDialog = true"
class="button"
[class.is-info]="!analysis.getTask()"
[class.is-warning]="analysis.getTask() && !analysis.getTask().startedAt"
[class.is-primary]="analysis.getTask() && analysis.getTask().startedAt && !analysis.getTask().done"
[class.is-success]="analysis.getTask() && analysis.getTask().done"
[disabled]="analysis.getCount() === 0 && !analysis.getTask()">
<span *ngIf="!analysis.getTask()">
class="button is-primary"
[disabled]="analysis.getCount() === 0">
<span>
<span class="icon">
<i class="fa fa-list"></i>
</span>
......@@ -196,67 +190,55 @@
</span>
</span>
<span *ngIf="analysis.getTask() && !analysis.getTask().startedAt">
<span class="icon">
<i class="fa fa-cog"></i>
</span>
<span>
Analysis queued...
</span>
</span>
</button>
</div>
<span *ngIf="analysis.getTask() && !analysis.getTask().done && analysis.getTask().startedAt">
<span class="icon">
<i class="fa fa-cog fa-spin"></i>
</span>
<span>
Analysis running ({{analysis.getTask().progress | percent}})
</span>
</span>
<span *ngIf="analysis.getTask() && analysis.getTask().done">
<span class="icon">
<i class="fa fa-check"></i>
</span>
<span>
Analysis done
</span>
</span>
</button>
<div class="card bar-large">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-filter" aria-hidden="true"></i>
</span> Tasks
</p>
</header>
<div class="card-content">
<app-task-list (token)="selectedAnalysisToken = $event"></app-task-list>
</div>
</div>
</div>
<div class="card bar-large">
<header class="card-header">
<p class="card-header-title">
<div class="card bar-large">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-filter" aria-hidden="true"></i>
</span> Selection
</p>
</header>
<div class="card-content overflow">
<table class="table">
<thead>
<tr>
<td>AC</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of analysis.getSelection()">
<td>{{p.proteinAc}}</td>
<td>
<button (click)="analysis.removeProtein(p)" class="button is-small is-danger">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</p>
</header>
<div class="card-content overflow">
<table class="table">
<thead>
<tr>
<td>AC</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of analysis.getSelection()">
<td>{{p.proteinAc}}</td>
<td>
<button (click)="analysis.removeProtein(p)" class="button is-small is-danger">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
......@@ -4,8 +4,6 @@ import {
ElementRef,
OnInit,
ViewChild,
Output,
EventEmitter,
HostListener
} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
......@@ -51,7 +49,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public queryItems = [];
public showAnalysisDialog = false;
public analysisWindow = false;
public selectedAnalysisToken: string | null = null;
public currentDataset = [];
private array = [0];
......@@ -88,15 +86,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
}
}
closeAnalysisWindow() {
this.analysisWindow = false;
}
showAnalysisWindow() {
this.analysisWindow = true;
}
constructor(private http: HttpClient,
private route: ActivatedRoute,
private router: Router,
......@@ -416,15 +405,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
};
}
// TODO: Remove this:
private random() {
const x = Math.sin(this.seed++) * 10000;
return x - Math.floor(x);
}
// Selection
// TODO: Improve usage of group ids, revise this after models have been changed to just protein
inSelection(proteinAc: string): boolean {
if (!this.proteinData || !proteinAc) {
return false;
......
......@@ -60,14 +60,14 @@ input.checkbox {
margin-left: 15px;
}
div.content.bar-left {
div.covex.bar-left {
float: left;
width: 350px;
height: calc(100vh - 102px);
overflow: auto;
}
div.content.bar-right {
div.covex.bar-right {
float: right;
width: 350px;
height: calc(100vh - 102px);
......@@ -96,14 +96,13 @@ div.card-content.overflow {
height: 500px;
}
div.content.left-window {
div.covex.left-window {
float: left;
width: calc(100vw - 350px - 2 * 20px);
height: 100%;
}
div.content.network {
div.covex.network {
width: calc(100% - 350px - 40px);
height: calc(100vh - 100px);
margin-left: 20px;
......@@ -127,13 +126,13 @@ div.center {
}
div.content.explorer {
div.covex.explorer {
height: calc(100vh - 90px);
margin-left: 20px;
margin-right: 20px;
}
div.analysis-view {
.analysis-view {
height: 100%;
width: calc(100% - 20px);
position: relative;
......@@ -143,5 +142,9 @@ div.analysis-view {
padding: 0;
}
i.fas.is-success {
}
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
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