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

Merge branch 'refactor-1' into 'master'

Refactor, Highlight Selected Task

See merge request covid-19/frontend!33
parents f25404fd 88d1ee67
Branches
Tags
No related merge requests found
Showing
with 116 additions and 158 deletions
import {Injectable} from '@angular/core';
import {Protein} from './pages/protein-network';
import {Protein, Task} from './interfaces';
import {Subject} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {environment} from '../environments/environment';
......@@ -13,9 +13,8 @@ export class AnalysisService {
private selectedProteins = new Map<string, Protein>();
private selectSubject = new Subject<{ protein: Protein, selected: boolean }>();
public tokens: any[] = [];
private stats: any;
public tasks: any[] = [];
public tokens: string[] = [];
public tasks: Task[] = [];
private intervalId: any;
......@@ -64,27 +63,6 @@ export class AnalysisService {
});
}
getTask(token): any {
this.tasks.forEach((task) => {
if (task.token === token) {
return task;
}
});
}
reset() {
this.tokens = null;
this.tasks = null;
this.stats = null;
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
getStats(): any {
return this.stats;
}
async startAnalysis(algorithm, parameters) {
const resp = await this.http.post<any>(`${environment.backend}task/`, {
algorithm,
......@@ -96,11 +74,13 @@ export class AnalysisService {
}
startWatching() {
this.intervalId = setInterval(async () => {
const watch = async () => {
if (this.tokens.length > 0) {
this.tasks = await this.getTasks();
}
}, 1000);
};
watch();
this.intervalId = setInterval(watch, 5000);
}
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { environment } from '../environments/environment';
const networkUrl = `${environment.backend}` + 'network/';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
async getNetwork(dataset: Array<[string, string]>) {
const data = JSON.stringify(dataset);
const params = new HttpParams().set('data', data);
return this.http.get(networkUrl, {params}).toPromise();
}
}
<div *ngIf="token">
<div class="card analysis" *ngIf="info && info.done">
<div class="card analysis" *ngIf="task && task.info.done">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -25,7 +25,7 @@
</footer>
</div>
<div class="card analysis" *ngIf="info && !info.startedAt">
<div class="card analysis" *ngIf="task && !task.info.startedAt">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -49,7 +49,7 @@
</footer>
</div>
<div class="card analysis" *ngIf="info && info.startedAt && !info.done">
<div class="card analysis" *ngIf="task && task.info.startedAt && !task.info.done">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......@@ -73,7 +73,7 @@
</footer>
</div>
<div class="card analysis" *ngIf="!info">
<div class="card analysis" *ngIf="!task">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
......
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';
import {Task} from '../../interfaces';
declare var vis: any;
......@@ -16,8 +16,7 @@ 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;
public task: Task | null = null;
@ViewChild('network', {static: false}) networkEl: ElementRef;
......@@ -33,13 +32,18 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
}
async ngOnChanges(changes: SimpleChanges) {
await this.refresh();
}
private async refresh() {
if (this.token) {
const {info, stats} = await this.getTask(this.token);
this.info = info;
this.stats = stats;
this.task = await this.getTask(this.token);
if (this.info && this.info.done) {
if (this.task && this.task.info.done) {
const result = await this.http.get<any>(`${environment.backend}result/?token=${this.token}`).toPromise();
this.networkEl.nativeElement.innerHTML = '';
this.network = null;
this.nodeData = {nodes: null, edges: null};
this.createNetwork(result);
}
}
......@@ -75,6 +79,10 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
};
this.network = new vis.Network(container, this.nodeData, options);
this.network.on('select', () => {
// TODO
});
}
private mapProteinToNode(protein: any): any {
......
import { Component, Input, Output, EventEmitter } from '@angular/core';
import {Protein} from '../../pages/protein-network';
import {Protein} from '../../interfaces';
@Component({
selector: 'app-query-component',
......
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {DataSet} from 'vis-data';
import {Edge, Effect, Protein} from '../../pages/protein-network';
@Component({
selector: 'app-select-dataset',
......
<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>
<a *ngFor="let task of analysis.tasks" class="list-item" [class.is-active]="task.token === token">
<div *ngIf="!task.info.startedAt" (click)="open(task.token)">
<b>Algorithm: {{task.info.algorithm}}</b><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>
<div *ngIf="task.info.startedAt && !task.info.done" (click)="open(task.token)">
<a>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 *ngIf="task.info.done" (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>
</div>
</a>
</div>
......
......@@ -10,8 +10,8 @@ import {AnalysisService} from '../../analysis.service';
export class TaskListComponent implements OnInit {
@Output() token: EventEmitter<any> = new EventEmitter();
@Input() token: string;
@Output() tokenChange: EventEmitter<string> = new EventEmitter();
constructor(public analysis: AnalysisService) {
}
......@@ -20,7 +20,8 @@ export class TaskListComponent implements OnInit {
}
open(token) {
this.token.emit(token);
this.token = token;
this.tokenChange.emit(token);
}
}
export interface Protein {
name: string;
proteinAc: string;
effects?: ViralProtein[];
x?: number;
y?: number;
}
export interface ViralProtein {
effectName: string;
virusName: string;
datasetName: string;
proteins?: Protein[];
x?: number;
y?: number;
}
export interface ProteinViralInteraction {
effectName: string;
virusName: string;
datasetName: string;
proteinAc: string;
}
export interface Task {
token: string;
info: {
algorithm: string;
parameters?: { [key: string]: any };
workerId?: string;
jobId?: string;
progress: number;
status: string;
createdAt: string;
startedAt: string;
finishedAt: string;
done: boolean;
failed: boolean;
};
stats: {
queuePosition: number;
queueLength: number;
};
}
import {HttpClient} from '@angular/common/http';
export interface Protein {
name: string;
proteinAc: string;
effects?: Effect[];
x?: number;
y?: number;
}
export interface Effect {
effectName: string;
virusName: string;
datasetName: string;
proteins?: Protein[];
x?: number;
y?: number;
}
export interface Edge {
effectName: string;
virusName: string;
datasetName: string;
proteinAc: string;
}
import {ProteinViralInteraction, ViralProtein, Protein} from './interfaces';
export function getDatasetFilename(dataset: Array<[string, string]>): string {
return `network-${JSON.stringify(dataset).replace(/[\[\]\",]/g, '')}.json`;
......@@ -30,7 +7,7 @@ export function getDatasetFilename(dataset: Array<[string, string]>): string {
export class ProteinNetwork {
constructor(public proteins: Protein[], public effects: Effect[], public edges: Edge[]) {
constructor(public proteins: Protein[], public effects: ViralProtein[], public edges: ProteinViralInteraction[]) {
}
public async loadPositions(http: HttpClient, dataset: Array<[string, string]>) {
......@@ -55,7 +32,7 @@ export class ProteinNetwork {
return this.proteins.find((p) => p.proteinAc === ac);
}
public getEffect(name: string, virus: string, dataset: string): Effect | undefined {
public getEffect(name: string, virus: string, dataset: string): ViralProtein | undefined {
return this.effects.find((eff) => eff.effectName === name && eff.virusName === virus && eff.datasetName === dataset);
}
......
......@@ -203,7 +203,7 @@
</p>
</header>
<div class="card-content">
<app-task-list (token)="selectedAnalysisToken = $event"></app-task-list>
<app-task-list [(token)]="selectedAnalysisToken"></app-task-list>
</div>
</div>
......
......@@ -3,15 +3,15 @@ import {
Component,
ElementRef,
OnInit,
ViewChild,
HostListener
ViewChild
} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Edge, Effect, getDatasetFilename, Protein, ProteinNetwork} from '../protein-network';
import {HttpClient} from '@angular/common/http';
import {ApiService} from '../../api.service';
import {ProteinViralInteraction, ViralProtein, Protein} from '../../interfaces';
import {ProteinNetwork, getDatasetFilename} from '../../main-network';
import {HttpClient, HttpParams} from '@angular/common/http';
import {AnalysisService} from '../../analysis.service';
import html2canvas from 'html2canvas';
import {environment} from '../../../environments/environment';
declare var vis: any;
......@@ -28,9 +28,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public geneNames: Array<string> = [];
public proteinNames: Array<string> = [];
public proteinAcs: Array<string> = [];
public watcher = 0;
public viralProteinCheckboxes: Array<{ checked: boolean; data: Effect }> = [];
public viralProteinCheckboxes: Array<{ checked: boolean; data: ViralProtein }> = [];
public proteinData: ProteinNetwork;
......@@ -41,8 +40,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
private network: any;
private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
private seed = 1; // TODO: Remove this
private dumpPositions = false;
public physicsEnabled = false;
......@@ -63,33 +60,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
@ViewChild('network', {static: false}) networkEl: ElementRef;
@HostListener('window:keydown', ['$event'])
handleKeyboardEvent1(event: KeyboardEvent) {
const keyName = event.key;
if (keyName === 'Control') {
this.watcher = 1;
// console.log(this.watcher);
}
}
@HostListener('window:keyup', ['$event'])
handleKeyboardEvent(event: KeyboardEvent) {
const keyName1 = event.key;
if (keyName1 === 'Control') {
this.watcher = 0;
// console.log(this.watcher);
}
}
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');
......@@ -127,6 +100,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.analysis.subscribe((protein, selected) => {
const nodeId = `pg_${protein.proteinAc}`;
const node = this.nodeData.nodes.get(nodeId);
if (!node) {
return;
}
const pos = this.network.getPositions([nodeId]);
node.x = pos[nodeId].x;
node.y = pos[nodeId].y;
......@@ -156,7 +132,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
private async getNetwork(dataset: Array<[string, string]>) {
this.currentDataset = dataset;
const data: any = await this.api.getNetwork(dataset);
const params = new HttpParams().set('data', JSON.stringify(dataset));
const data = await this.http.get<any>(`${environment.backend}network/`, {params}).toPromise();
this.proteins = data.proteins;
this.effects = data.effects;
this.edges = data.edges;
......@@ -237,25 +214,18 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
};
this.network = new vis.Network(container, this.nodeData, options);
this.network.on('click', (properties) => {
this.network.on('select', (properties) => {
const id: Array<string> = properties.nodes;
// TODO use groupID
if (id.length > 0) {
if (id[0].startsWith('pg_')) {
const protein = this.proteinData.getProtein(id[0].substr(3));
this.openSummary(protein, false);
// tslint:disable-next-line:no-console
console.log(this.currentProteinAc);
if (this.watcher === 1) {
if (properties.event.srcEvent.ctrlKey) {
if (this.inSelection(protein.proteinAc) === true) {
// tslint:disable-next-line:no-console
console.log(this.removeFromSelection(protein.proteinAc));
this.removeFromSelection(protein.proteinAc);
} else {
// tslint:disable-next-line:no-console
console.log(this.addToSelection(protein.proteinAc));
// console.log(this.removeFromSelection(this.currentProteinAc));
// tslint:disable-next-line:no-console
console.log(this.analysis.getCount());
this.addToSelection(protein.proteinAc);
this.analysis.getCount();
}
}
} else {
......@@ -291,7 +261,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
const connectedProteinAcs = new Set<string>();
this.viralProteinCheckboxes.forEach((cb) => {
const effects: Array<Effect> = [];
const effects: Array<ViralProtein> = [];
this.proteinData.effects.forEach((effect) => {
if (effect.effectName === cb.data.effectName) {
effects.push(effect);
......@@ -325,11 +295,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
if (!found) {
const node = this.mapProteinToNode(protein);
// this.nodeData.nodes.add(node);
addNodes.set(node.id, node);
}
} else if (found) {
// this.nodeData.nodes.remove(nodeId);
removeIds.add(nodeId);
}
}
......@@ -365,7 +333,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
};
}
private mapEffectToNode(effect: Effect): any {
private mapEffectToNode(effect: ViralProtein): any {
return {
id: `eff_${effect.effectName}_${effect.virusName}_${effect.datasetName}`,
label: `${effect.effectName} (${effect.virusName}, ${effect.datasetName})`,
......@@ -375,7 +343,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
};
}
private mapEdge(edge: Edge): any {
private mapEdge(edge: ProteinViralInteraction): any {
return {
from: `pg_${edge.proteinAc}`,
to: `eff_${edge.effectName}_${edge.virusName}_${edge.datasetName}`,
......@@ -441,8 +409,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
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) {
html2canvas(elem).then((canvas) => {
const generatedImage = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
const a = document.createElement('a');
a.href = generatedImage;
......@@ -452,6 +419,4 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
});
}
}
......@@ -88,12 +88,12 @@ div.card.bar-medium {
div.card.bar-large {
margin-bottom: 15px;
height: 600px;
max-height: 600px;
}
div.card-content.overflow {
overflow: auto;
height: 500px;
max-height: 500px;
}
div.covex.left-window {
......
......@@ -73,6 +73,8 @@
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"no-unused-expression": true,
"no-unused-variable": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment