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

Add analysis window

parent b04a929a
Branches
Tags
No related merge requests found
......@@ -1464,6 +1464,21 @@
"to-fast-properties": "^2.0.0"
}
},
"@creativebulma/bulma-collapsible": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@creativebulma/bulma-collapsible/-/bulma-collapsible-1.0.4.tgz",
"integrity": "sha512-aNqSwyuJxshoIc4oD3wJ3eRqOANRweYfpzqmef/fj5tf0Yn7UVx99yh8ovY6vhB8Il31bFcc7f/eRfJvEMNUPw==",
"requires": {
"bulma": "^0.8.1"
},
"dependencies": {
"bulma": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.1.tgz",
"integrity": "sha512-Afi2zv4DKmNSYfmx55V+Mtnt8+WfR8Rs65kWArmzEuWP7vNr7dSAEDI+ORZlgOR1gueNZwpKaPdUi4ZiTNwgPA=="
}
}
},
"@fortawesome/angular-fontawesome": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.6.1.tgz",
......
......@@ -15,6 +15,7 @@ import {QueryComponent} from './components/query/query.component';
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';
@NgModule({
......@@ -26,6 +27,7 @@ import { SelectDatasetComponent } from './components/select-dataset/select-datas
QueryComponent,
ProteinAnalysisComponent,
SelectDatasetComponent,
AnalysisWindowComponent,
],
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>
</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>
.analysis {
position: absolute;
height: 100%;
width: 100%;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AnalysisWindowComponent } from './analysis-window.component';
describe('AnalysisWindowComponent', () => {
let component: AnalysisWindowComponent;
let fixture: ComponentFixture<AnalysisWindowComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AnalysisWindowComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalysisWindowComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
selector: 'app-analysis-window',
templateUrl: './analysis-window.component.html',
styleUrls: ['./analysis-window.component.scss']
})
export class AnalysisWindowComponent implements OnInit {
@Input() analysisWindow: boolean;
@Output() closeWindow = new EventEmitter<boolean>();
constructor() { }
ngOnInit(): void {
}
closeAnalysisWindow() {
this.closeWindow.emit(this.analysisWindow);
}
discard() {
}
export() {
}
}
......@@ -2,9 +2,11 @@
<div class="content explorer">
<div class="content left-window">
<div>
<div class="content bar-left">
<div class="card bar">
<div class="card bar-small">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -14,17 +16,18 @@
</header>
<div class="card-content">
<div class="content">
<app-select-dataset [datasetItems]="datasetItems" (selectDataset)="createNetwork($event)"></app-select-dataset>
<app-select-dataset [datasetItems]="datasetItems"
(selectDataset)="createNetwork($event)"></app-select-dataset>
</div>
</div>
</div>
<div class="card bar">
<div class="card bar-medium">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-info" aria-hidden="true"></i>
</span> Info
</span> Network Overview
</p>
</header>
<div class="card-content">
......@@ -32,19 +35,19 @@
<nav class="level" *ngIf="proteinData">
<div class="level-item has-text-centered">
<div>
<p class="heading">Viral Proteins</p>
<p class="heading">Viral<br>Proteins</p>
<p class="title"> {{ proteinData.effects.length }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Host Proteins</p>
<p class="heading">Host<br>Proteins</p>
<p class="title">{{ proteinData.proteins.length }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Interactions</p>
<p class="heading">Virus-Host<br>Interactions</p>
<p class="title">{{ proteinData.edges.length }}</p>
</div>
</div>
......@@ -53,7 +56,7 @@
</div>
</div>
<div class="card bar">
<div class="card bar-small">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -73,7 +76,7 @@
</div>
</div>
<div class="card bar">
<div class="card bar-large">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -100,30 +103,47 @@
</button>
</footer>
</div>
</div>
<div class="content network">
<div class="card bar">
<div class="card network">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-cog" aria-hidden="true"></i>
</span> Settings
Protein-Protein Interaction Network
</p>
</header>
<div class="card-content">
<div class="content">
<div class="card-image" id="0">
<div class="network center" #network>
<button class="button is-loading center">Loading</button>
</div>
</div>
<footer class="card-footer">
<button (click)="toCanvas()" class="card-footer-item button is-primary">
<span class="icon">
<i class="fas fa-cloud-download-alt" aria-hidden="true"></i>
</span>
</button>
<label class="checkbox">
<input type="checkbox" class="checkbox" [(ngModel)]="physicsEnabled"
(ngModelChange)="physicsEnabled = $event; updatePhysicsEnabled()">
Physics enabled
</label>
</footer>
</div>
</div>
</div>
</div>
<div class="analysis-view" [hidden]="!analysisWindow">
<app-analysis-window (closeWindow)="closeAnalysisWindow()"></app-analysis-window>
</div>
</div>
<div class="content bar-right">
<div class="card bar">
<div class="card bar-medium">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -143,22 +163,17 @@
{{ proteinAc }}
</a>
</p>
<button class="button is-primary" *ngIf="!inSelection(currentProteinAc)"
(click)="addToSelection(currentProteinAc)">Add to Analysis
</button>
<button class="button is-danger" *ngIf="inSelection(currentProteinAc)"
(click)="removeFromSelection(currentProteinAc)">Remove from Analysis
</button>
</div>
<div *ngIf="!showDetails">
Please select a node for further information.
<a (click)="showAnalysisWindow()"> Open Analysis Window </a>
</div>
</div>
</div>
<div class="card bar">
<div class="card bar-medium">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -175,7 +190,7 @@
</div>
</div>
<div class="card bar">
<div class="card bar-medium">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -184,8 +199,13 @@
</p>
</header>
<div class="card-content">
<p>Hold down the CTRL button to select multiple proteins.</p>
<button class="button is-success" *ngIf="!inSelection(currentProteinAc)"
(click)="addToSelection(currentProteinAc)">Add to Analysis
</button>
<button class="button is-danger" *ngIf="inSelection(currentProteinAc)"
(click)="removeFromSelection(currentProteinAc)">Remove from Analysis
</button>
<p></p>
<button (click)="showAnalysisDialog = true"
class="button"
[class.is-info]="!analysis.getTask()"
......@@ -234,29 +254,4 @@
</div>
<div class="content network">
<div class="card network">
<header class="card-header">
<p class="card-header-title">
Protein-Protein Interaction Network
</p>
</header>
<div class="card-content">
<div id= "0" class="card-image">
<div class="network center" #network>
<button class="button is-loading center">Loading</button>
</div>
</div>
<footer class="card-footer">
<button (click)="toCanvas()" class="card-footer-item button is-primary">
<span class="icon">
<i class="fas fa-cloud-download-alt" aria-hidden="true"></i>
</span>
</button>
</footer>
</div>
</div>
</div>
</div>
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild, Output, EventEmitter, HostListener} from '@angular/core';
import {
AfterViewInit,
Component,
ElementRef,
OnInit,
ViewChild,
Output,
EventEmitter,
HostListener
} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Edge, Effect, getDatasetFilename, Protein, ProteinNetwork} from '../protein-network';
import {HttpClient} from '@angular/common/http';
......@@ -9,7 +18,6 @@ import html2canvas from 'html2canvas';
declare var vis: any;
@Component({
selector: 'app-explorer-page',
templateUrl: './explorer-page.component.html',
......@@ -17,64 +25,6 @@ declare var vis: any;
})
export class ExplorerPageComponent implements OnInit, AfterViewInit {
constructor(private http: HttpClient,
private route: ActivatedRoute,
private router: Router,
private api: ApiService,
public analysis: AnalysisService) {
this.geneNames.push('IFI16');
this.proteinNames.push('Gamma-interface-inducible protein 16');
this.proteinAcs.push('Q16666');
this.route.queryParams.subscribe(async (params) => {
this.dumpPositions = params.dumpPositions;
this.physicsEnabled = !!this.dumpPositions;
const protein = params.protein;
if (!protein) {
// In this case, the URL is just `/explorer`
// Therefore, we do not show a modal
this.showDetails = false;
return;
}
// In this case, the URL is `/explorer/<protein>`
if (this.currentProteinAc === protein) {
// The protein group is the same as before, so we do not need to do anything
// TODO Also highlight node when reloading the page/sharing the URL
return;
}
// We have a new protein id, so we need to load it and show the modal...
this.currentProteinAc = protein;
// TODO: Perform call here for 'protein'...
// this.zoomToNode(protein)
this.showDetails = true;
});
this.analysis.subscribe((protein, selected) => {
const nodeId = `pg_${protein.proteinAc}`;
const node = this.nodeData.nodes.get(nodeId);
const pos = this.network.getPositions([nodeId]);
node.x = pos[nodeId].x;
node.y = pos[nodeId].y;
if (selected) {
if (node) {
node.color = '#c42eff';
this.nodeData.nodes.update(node);
}
} else {
if (node) {
node.color = '#e2b600';
this.nodeData.nodes.update(node);
}
}
});
}
public showDetails = false;
public currentProteinAc = '';
public geneNames: Array<string> = [];
......@@ -101,6 +51,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public queryItems = [];
public showAnalysisDialog = false;
public analysisWindow = false;
public currentDataset = [];
private array = [0];
......@@ -111,25 +63,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
{label: 'CoV2', datasets: 'Krogan', data: [['Krogan', 'SARS-CoV2']]},
{label: 'CoV2', datasets: 'TUM', data: [['TUM', 'SARS-CoV2']]}];
@ViewChild('network', {static: false}) networkEl: ElementRef;
public toCanvas() {
this.array.forEach((key, index) => {
const elem = document.getElementById(index.toString());
// tslint:disable-next-line:only-arrow-functions
html2canvas(elem).then(function(canvas) {
const generatedImage = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
const a = document.createElement('a');
a.href = generatedImage;
a.download = `Network.png`;
a.click();
});
});
}
@HostListener('window:keydown', ['$event'])
handleKeyboardEvent1(event: KeyboardEvent) {
......@@ -153,6 +88,73 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
}
}
closeAnalysisWindow() {
this.analysisWindow = false;
}
showAnalysisWindow() {
this.analysisWindow = true;
}
constructor(private http: HttpClient,
private route: ActivatedRoute,
private router: Router,
private api: ApiService,
public analysis: AnalysisService) {
this.geneNames.push('IFI16');
this.proteinNames.push('Gamma-interface-inducible protein 16');
this.proteinAcs.push('Q16666');
this.route.queryParams.subscribe(async (params) => {
this.dumpPositions = params.dumpPositions;
this.physicsEnabled = !!this.dumpPositions;
const protein = params.protein;
if (!protein) {
// In this case, the URL is just `/explorer`
// Therefore, we do not show a modal
this.showDetails = false;
return;
}
// In this case, the URL is `/explorer/<protein>`
if (this.currentProteinAc === protein) {
// The protein group is the same as before, so we do not need to do anything
// TODO Also highlight node when reloading the page/sharing the URL
return;
}
// We have a new protein id, so we need to load it and show the modal...
this.currentProteinAc = protein;
// TODO: Perform call here for 'protein'...
// this.zoomToNode(protein)
this.showDetails = true;
});
this.analysis.subscribe((protein, selected) => {
const nodeId = `pg_${protein.proteinAc}`;
const node = this.nodeData.nodes.get(nodeId);
const pos = this.network.getPositions([nodeId]);
node.x = pos[nodeId].x;
node.y = pos[nodeId].y;
if (selected) {
if (node) {
node.color = '#c42eff';
this.nodeData.nodes.update(node);
}
} else {
if (node) {
node.color = '#e2b600';
this.nodeData.nodes.update(node);
}
}
});
}
ngOnInit() {
}
......@@ -456,4 +458,20 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.analysis.removeProtein(protein);
}
public toCanvas() {
this.array.forEach((key, index) => {
const elem = document.getElementById(index.toString());
// tslint:disable-next-line:only-arrow-functions
html2canvas(elem).then(function(canvas) {
const generatedImage = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
const a = document.createElement('a');
a.href = generatedImage;
a.download = `Network.png`;
a.click();
});
});
}
}
......@@ -7,6 +7,14 @@
$family-sans-serif: "Varela Round", sans-serif;
@import "~bulma/bulma.sass";
html {
overflow: hidden;
}
body {
overflow: hidden;
}
.hero.is-primary {
background-color: #118AB2;
background-image: url("assets/logo.png"), linear-gradient(to left, #f2fcfe, #118AB2);
......@@ -54,35 +62,53 @@ input.checkbox {
div.content.bar-left {
float: left;
width: 15%;
height: calc(100vh - 30%);
width: 350px;
height: calc(100vh - 102px);
}
div.content.bar-right {
float: right;
width: 15%;
width: 350px;
}
div.card.bar {
margin-bottom: 21px;
div.card.bar-small {
margin-bottom: 15px;
word-wrap: break-word;
height: 130px;
}
div.card.bar-medium {
margin-bottom: 15px;
word-wrap: break-word;
height: 170px;
}
div.card.bar-large {
margin-bottom: 15px;
height: calc(100vh - 660px);
}
div.content.left-window {
float: left;
width: calc(100vw - 350px - 2 * 20px);
height: 100%;
}
div.content.network {
width: 68%;
width: calc(100% - 350px - 40px);
height: calc(100vh - 100px);
margin-left: auto;
margin-right: auto;
margin-left: 20px;
margin-right: 20px;
float: right;
}
div.card.network {
width: 100%;
height: calc(100vh - 80px);
height: calc(100vh - 85px);
}
div.network {
height: calc(100vh - 200px);
height: calc(100vh - 210px);
}
div.center {
......@@ -93,9 +119,20 @@ div.center {
div.content.explorer {
height: calc(100vh - 90px);
margin-left: 20px;
margin-right: 20px;
}
div.analysis-view {
height: 100%;
width: calc(100% - 20px);
position: relative;
margin-top: 0;
/* Just for looks*/
z-index: 1000;
padding: 0;
}
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.
Please register or to comment