From da5b859697ff9397c599ccfe130e2ba3eb3c0716 Mon Sep 17 00:00:00 2001
From: Julian Matschinske <ge93nar@mytum.de>
Date: Mon, 6 Apr 2020 21:19:23 +0200
Subject: [PATCH] Adapt Analysis Window

---
 src/app/analysis.service.ts                   |   4 +
 .../analysis-window.component.html            |   2 +-
 .../analysis-window.component.ts              | 248 ++++++++----------
 .../launch-analysis.component.ts              |   5 +-
 .../select-dataset.component.html             |   2 +-
 .../select-dataset.component.ts               |   2 -
 .../task-list/task-list.component.html        |  49 ++--
 .../task-list/task-list.component.scss        |  10 +
 src/app/interfaces.ts                         |   4 +-
 .../explorer-page.component.html              |   8 +-
 .../explorer-page/explorer-page.component.ts  |  26 +-
 11 files changed, 181 insertions(+), 179 deletions(-)

diff --git a/src/app/analysis.service.ts b/src/app/analysis.service.ts
index e90aade5..f1e5a289 100644
--- a/src/app/analysis.service.ts
+++ b/src/app/analysis.service.ts
@@ -62,6 +62,10 @@ export class AnalysisService {
     return this.selectedProteins.has(protein.proteinAc);
   }
 
+  idInSelection(id: string): boolean {
+    return this.selectedProteins.has(id);
+  }
+
   removeProtein(protein: Protein) {
     if (this.selectedProteins.delete(`${protein.proteinAc}`)) {
       this.selectSubject.next({protein, selected: false});
diff --git a/src/app/components/analysis-window/analysis-window.component.html b/src/app/components/analysis-window/analysis-window.component.html
index f6e4f608..ba091706 100644
--- a/src/app/components/analysis-window/analysis-window.component.html
+++ b/src/app/components/analysis-window/analysis-window.component.html
@@ -44,7 +44,7 @@
               </button>
             </p>
             <p class="control">
-              <button class="button is-danger is-rounded" (click)="analysis.removeTask(token)">
+              <button class="button is-danger is-rounded" (click)="analysis.removeTask(token); close()">
             <span class="icon">
               <i class="fas fa-trash" aria-hidden="true"></i>
             </span>
diff --git a/src/app/components/analysis-window/analysis-window.component.ts b/src/app/components/analysis-window/analysis-window.component.ts
index e89bce3a..e9f18dd9 100644
--- a/src/app/components/analysis-window/analysis-window.component.ts
+++ b/src/app/components/analysis-window/analysis-window.component.ts
@@ -12,7 +12,7 @@ import {
 import {HttpClient} from '@angular/common/http';
 import {environment} from '../../../environments/environment';
 import {AnalysisService} from '../../analysis.service';
-import {Protein, Task} from '../../interfaces';
+import {Protein, Task, NodeType} from '../../interfaces';
 
 declare var vis: any;
 
@@ -34,7 +34,6 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
   private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
   private drugNodes = [];
   public showDrugs = false;
-  private result: any;
 
   constructor(private http: HttpClient, public analysis: AnalysisService) {
   }
@@ -51,13 +50,53 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
       this.task = await this.getTask(this.token);
 
       if (this.task && this.task.info.done) {
-        const result = await this.http.get<any>(`${environment.backend}result/?token=${this.token}`).toPromise();
-        this.result = result;
+        const result = await this.http.get<any>(`${environment.backend}task_result/?token=${this.token}`).toPromise();
+
+        // Reset
+        this.nodeData = {nodes: null, edges: null};
         this.networkEl.nativeElement.innerHTML = '';
         this.network = null;
         this.showDrugs = false;
-        this.nodeData = {nodes: null, edges: null};
-        this.createNetwork(result);
+
+        // Create
+        const {nodes, edges} = this.createNetwork(result);
+        this.nodeData.nodes = new vis.DataSet(nodes);
+        this.nodeData.edges = new vis.DataSet(edges);
+
+        const container = this.networkEl.nativeElement;
+        const options = {};
+
+        this.network = new vis.Network(container, this.nodeData, options);
+        this.network.on('select', (properties) => {
+          const selectedNodes = this.nodeData.nodes.get(properties.nodes);
+          if (selectedNodes.length > 0) {
+            if (selectedNodes[0].nodeType === 'host') {
+              const protein: Protein = {name: '', proteinAc: selectedNodes[0].id};
+              if (properties.event.srcEvent.ctrlKey) {
+                if (this.analysis.inSelection(protein) === true) {
+                  this.analysis.removeProtein(protein);
+                } else {
+                  this.analysis.addProtein(protein);
+                  this.analysis.getCount();
+                }
+              }
+            }
+          }
+        });
+
+        this.analysis.subscribe((protein, selected) => {
+          const nodeId = `${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;
+          const {color} = this.getNodeLooks(nodeId, node.nodeType, node.isSeed);
+          node.color = color;
+          this.nodeData.nodes.update(node);
+        });
       }
     }
   }
@@ -79,161 +118,92 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
 
   }
 
-  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);
-    this.network.on('select', (properties) => {
-      const id: Array<string> = properties.nodes;
-      if (id.length > 0) {
-        if (id[0].startsWith('p_')) {
-          const protein = this.getProtein(id[0].substr(2));
-          if (properties.event.srcEvent.ctrlKey) {
-            if (this.inSelection(protein.proteinAc) === true) {
-              this.removeFromSelection(protein.proteinAc);
-            } else {
-              this.addToSelection(protein.proteinAc);
-              this.analysis.getCount();
-            }
-          }
-        }
-      }
-    });
-
-    this.analysis.subscribe((protein, selected) => {
-      const nodeId = `p_${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;
-      if (selected) {
-        node.color = '#48C774';
-        this.nodeData.nodes.update(node);
-      } else {
-        node.color = '#e2b600';
-        this.nodeData.nodes.update(node);
-      }
-    });
-  }
-
-  inSelection(proteinAc: string): boolean {
-    if (!this.result.proteins || !proteinAc) {
-      return false;
+  public inferNodeType(nodeId: string): 'host' | 'virus' | 'drug' {
+    if (nodeId.indexOf('-') !== -1 || nodeId.indexOf('_') !== -1) {
+      return 'virus';
     }
-    const protein = this.getProtein(proteinAc);
-    if (!protein) {
-      return false;
-    }
-    return this.analysis.inSelection(protein);
+    return 'host';
   }
 
-  addToSelection(proteinAc: string) {
-    if (!this.result.proteins || !proteinAc) {
-      return false;
-    }
-    const protein = this.getProtein(proteinAc);
-    if (!protein) {
-      return false;
-    }
-    this.analysis.addProtein(protein);
-  }
+  public createNetwork(result: any): { edges: any[], nodes: any[] } {
+    const nodes = [];
+    const edges = [];
 
-  removeFromSelection(proteinAc: string) {
-    if (!this.result.proteins || !proteinAc) {
-      return false;
-    }
-    const protein = this.getProtein(proteinAc);
-    if (!protein) {
-      return false;
-    }
-    this.analysis.removeProtein(protein);
-  }
+    const nodeAttributes = result.nodeAttributes || [];
 
-  public getProtein(ac: string): Protein | undefined {
-    return this.result.proteins.find((p) => p.proteinAc === ac);
-  }
+    for (let i = 0; i < result.networks.length; i++) {
+      const network = result.networks[i];
 
+      const attributes = nodeAttributes[i] || {};
+      const nodeTypes = attributes.nodeTypes || {};
+      const isSeed = attributes.isSeed || {};
+      const scores = attributes.scores || {};
 
-  private mapProteinToNode(protein: any): any {
-    let color = '#e2b600';
-    if (this.analysis.inSelection(protein)) {
-      color = '#48C774';
-    }
-    return {
-      id: `p_${protein.proteinAc}`,
-      label: `${protein.proteinAc}`,
-      size: 10, color, shape: 'ellipse', shadow: false,
-    };
-  }
+      for (const node of network.nodes) {
+        nodes.push(this.mapNode(node, nodeTypes[node] || this.inferNodeType(node), isSeed[node], scores[node]));
+      }
 
-  private mapDrugToNode(drug: any): any {
-    let color = '#ffffff';
-    if (drug.status === 'investigational') {
-      color = '#ffa066';
-    } else if (drug.status === 'approved') {
-      color = '#a0ff66';
+      for (const edge of network.edges) {
+        edges.push(this.mapEdge(edge));
+      }
     }
-    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'},
+      nodes,
+      edges,
     };
   }
 
-  private mapDataToNodes(result: any): { nodes: any[], edges: any[] } {
-    const nodes = [];
-    const edges = [];
+  private getNodeLooks(nodeId: string, nodeType: NodeType, isSeed: boolean):
+    { color: string, shape: string, size: number, font: any, shadow: boolean } {
+    let color = '';
+    let shape = '';
+    let size = 10;
+    let font = {};
+    let shadow = false;
 
-    for (const protein of result.proteins) {
-      nodes.push(this.mapProteinToNode(protein));
+    if (nodeType === 'host') {
+      shape = 'ellipse';
+      if (this.analysis.idInSelection(nodeId)) {
+        color = '#c7661c';
+      } else {
+        color = '#e2b600';
+      }
+      size = 10;
+    } else if (nodeType === 'virus') {
+      shape = 'box';
+      color = '#118AB2';
+      size = 12;
+      font = {color: 'white'};
+      shadow = true;
+    } else if (nodeType === 'drug') {
+      shape = 'ellipse';
+      color = '#26b28b';
+      size = 6;
     }
 
-    this.drugNodes = [];
-    for (const drug of result.drugs) {
-      this.drugNodes.push(this.mapDrugToNode(drug));
+    if (isSeed) {
+      color = '#c064c7';
     }
 
-    for (const network of result.networks) {
-      for (const edge of network.ppEdges) {
-        edges.push(this.mapProteinProteinInteractionToEdge(edge));
-      }
-    }
+    return {color, shape, size, font, shadow};
+  }
 
-    for (const edge of result.dpEdges) {
-      edges.push(this.mapDrugProteinInteractionToEdge(edge));
-    }
+  private mapNode(nodeId: any, nodeType?: NodeType, isSeed?: boolean, score?: number): any {
+    const {shape, color, size, font, shadow} = this.getNodeLooks(nodeId, nodeType, isSeed);
+    return {
+      id: nodeId,
+      label: nodeId,
+      size, color, shape, font, shadow,
+      nodeType, isSeed,
+    };
+  }
 
+  private mapEdge(edge: any): any {
     return {
-      nodes,
-      edges,
+      from: `${edge.from}`,
+      to: `${edge.to}`,
+      color: {color: '#afafaf', highlight: '#854141'},
     };
   }
 
diff --git a/src/app/components/launch-analysis/launch-analysis.component.ts b/src/app/components/launch-analysis/launch-analysis.component.ts
index 65c54814..a0a884f1 100644
--- a/src/app/components/launch-analysis/launch-analysis.component.ts
+++ b/src/app/components/launch-analysis/launch-analysis.component.ts
@@ -30,7 +30,6 @@ export class LaunchAnalysisComponent implements OnInit {
   public multisteinerStrain = 'SARS_CoV2';
   public multisteinerNumTrees = 5;
 
-
   constructor(public analysis: AnalysisService) {
   }
 
@@ -44,13 +43,13 @@ export class LaunchAnalysisComponent implements OnInit {
 
   public async startTask() {
     const parameters: any = {
-      proteins: this.analysis.getSelection().map((protein) => protein.proteinAc),
+      seeds: this.analysis.getSelection().map((protein) => protein.proteinAc),
     };
 
     if (this.algorithm === 'dummy') {
       // No parameters for dummy
     } else if (this.algorithm === 'trustrank') {
-      parameters.strain = this.trustrankStrain;
+      parameters.strain_or_drugs = this.trustrankStrain;
       parameters.datasets = [];
       parameters.ignored_edge_types = [];
       parameters.damping_factor = this.trustrankDampingFactor;
diff --git a/src/app/components/select-dataset/select-dataset.component.html b/src/app/components/select-dataset/select-dataset.component.html
index 9c0a7789..b34ccf14 100644
--- a/src/app/components/select-dataset/select-dataset.component.html
+++ b/src/app/components/select-dataset/select-dataset.component.html
@@ -1,5 +1,5 @@
 <div class="content">
-  <ng-select [items]="datasetItems" bindLabel="label" [virtualScroll]="true" class="custom"
+  <ng-select [items]="datasetItems" bindLabel="id" [virtualScroll]="true" class="custom"
              placeholder="Select..." [ngModel]="selectedDataset" (ngModelChange)="select($event)">
     <ng-template ng-option-tmp let-item="item">
       {{item.label}} <br/>
diff --git a/src/app/components/select-dataset/select-dataset.component.ts b/src/app/components/select-dataset/select-dataset.component.ts
index 4b669c79..d2f32719 100644
--- a/src/app/components/select-dataset/select-dataset.component.ts
+++ b/src/app/components/select-dataset/select-dataset.component.ts
@@ -14,10 +14,8 @@ export class SelectDatasetComponent {
   @Input() datasetItems: Array<{label: string, datasets: string, data: Array<[string, string]>}>;
 
   public select(selectionItem) {
-    // console.log(selectionItem);
     this.selectedDataset = selectionItem;
     this.selectedDatasetChange.emit(selectionItem);
-
   }
 
 }
diff --git a/src/app/components/task-list/task-list.component.html b/src/app/components/task-list/task-list.component.html
index 7dfbe838..eb672041 100644
--- a/src/app/components/task-list/task-list.component.html
+++ b/src/app/components/task-list/task-list.component.html
@@ -2,26 +2,41 @@
   <div class="list is-hoverable">
     <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)">
-        <span><b>{{task.info.algorithm}} (Queued)</b></span>
-        <span class="icon"><i class="fas fa-pause" aria-hidden="true"></i></span><br>
-        Queue Length: {{task.stats.queueLength}}<br>
-        Queue Position:{{task.stats.queuePosition}}
+        <p>
+          <span class="is-capitalized">{{task.info.algorithm}}</span>
+          <span class="icon is-pulled-right"><i class="fas fa-pause" aria-hidden="true"></i></span>
+        </p>
+        <p>
+          <small>Queue position: {{task.stats.queuePosition}}/{{task.stats.queueLength}}</small>
+        </p>
       </div>
-      <div *ngIf="task.info.startedAt && !task.info.done && !task.info.failed" (click)="open(task.token)">
-        <span><b>{{task.info.algorithm}} ({{task.info.startedAt | date :'short'}})</b></span>
-        <span class="icon"><i class="fas fa-spinner" aria-hidden="true"></i></span>
-        <br>
-        <progress class="progress is-primary" [value]="task.info.progress * 100" max="100"></progress>
+      <div *ngIf="!task.info.done && !task.info.failed && task.info.startedAt" (click)="open(task.token)">
+        <p>
+          <span class="is-capitalized">{{task.info.algorithm}}</span>
+          <span class="icon is-pulled-right"><i class="fas fa-spinner fa-spin" aria-hidden="true"></i></span>
+        </p>
+        <p>
+          <small>Started {{task.info.startedAt | date :'short'}}</small>
+        </p>
+        <progress class="progress is-primary" [value]="task.info.progress * 100" max="100">Test</progress>
       </div>
-      <div *ngIf="task.info.failed && task.info.finishedAt == null">
-        <span><b>{{task.info.algorithm}} ({{task.info.startedAt | date :'short'}})</b></span>
-        <span class="icon"><i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
-        </span>
+      <div *ngIf="task.info.done" (click)="open(task.token)">
+        <p>
+          <span class="is-capitalized">{{task.info.algorithm}}</span>
+          <span class="icon is-pulled-right"><i class="fas fa-check" aria-hidden="true"></i></span>
+        </p>
+        <p>
+          <small>Finished {{task.info.finishedAt | date :'short'}}</small>
+        </p>
       </div>
-      <div *ngIf="task.info.done && !task.info.failed" (click)="open(task.token)">
-        <span><b>{{task.info.algorithm}} ({{task.info.startedAt | date :'short'}})</b></span>
-        <span class="icon"><i class="fas fa-check" aria-hidden="true"></i>
-        </span>
+      <div *ngIf="task.info.failed && task.info.finishedAt == null">
+        <p>
+          <span class="is-capitalized">{{task.info.algorithm}}</span>
+          <span class="icon is-pulled-right"><i class="fas fa-exclamation-triangle" aria-hidden="true"></i></span>
+        </p>
+        <p class="has-text-danger">
+          <small>{{task.info.status}}</small>
+        </p>
       </div>
     </a>
   </div>
diff --git a/src/app/components/task-list/task-list.component.scss b/src/app/components/task-list/task-list.component.scss
index e69de29b..77578f19 100644
--- a/src/app/components/task-list/task-list.component.scss
+++ b/src/app/components/task-list/task-list.component.scss
@@ -0,0 +1,10 @@
+.list {
+  p {
+    margin-bottom: 0;
+  }
+
+  progress {
+    margin-top: 5px;
+    margin-bottom: 5px;
+  }
+}
diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts
index b8e87b54..6a5341ed 100644
--- a/src/app/interfaces.ts
+++ b/src/app/interfaces.ts
@@ -1,3 +1,5 @@
+export type NodeType = 'host' | 'virus' | 'drug';
+
 export interface Protein {
   name: string;
   proteinAc: string;
@@ -25,7 +27,7 @@ export interface ProteinViralInteraction {
 export interface Task {
   token: string;
   info: {
-    algorithm: string;
+    algorithm: 'trustrank' | 'multisteiner' | 'keypathwayminer';
     parameters?: { [key: string]: any };
 
     workerId?: string;
diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html
index 80a425d9..b9d22f7d 100644
--- a/src/app/pages/explorer-page/explorer-page.component.html
+++ b/src/app/pages/explorer-page/explorer-page.component.html
@@ -266,9 +266,9 @@
     <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
+          <span class="icon">
+            <i class="fas fa-filter" aria-hidden="true"></i>
+          </span> Tasks
         </p>
         <a (click)="collabsTask = !collabsTask" data-action="collapse" class="card-header-icon is-hidden-fullscreen" aria-label="more options">
           <span class="icon">
@@ -281,7 +281,7 @@
           <app-task-list [(token)]="selectedAnalysisToken"></app-task-list>
         </div>
         <footer class="card-footer">
-          <a (click)="analysis.removeAllTasks();" class="card-footer-item has-text-danger">
+          <a (click)="analysis.removeAllTasks(); selectedAnalysisToken = null;" class="card-footer-item has-text-danger">
             <span class="icon">
                 <i class="fa fa-trash"></i>
               </span>
diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts
index 92db3b0f..6c2f6c1b 100644
--- a/src/app/pages/explorer-page/explorer-page.component.ts
+++ b/src/app/pages/explorer-page/explorer-page.component.ts
@@ -58,15 +58,19 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
   public currentDataset = [];
   private screenshotArray = [0];
 
-  public datasetItems: Array<{ label: string, datasets: string, data: Array<[string, string]> }> = [
-    {label: 'All', datasets: 'TUM & Krogan', data: [['TUM', 'HCoV'], ['TUM', 'SARS-CoV2'], ['Krogan', 'SARS-CoV2']]},
-    {label: 'HCoV', datasets: 'TUM', data: [['TUM', 'HCoV']]},
-    {label: 'CoV2', datasets: 'TUM & Krogan', data: [['TUM', 'SARS-CoV2'], ['Krogan', 'SARS-CoV2']]},
-    {label: 'CoV2', datasets: 'Krogan', data: [['Krogan', 'SARS-CoV2']]},
-    {label: 'CoV2', datasets: 'TUM', data: [['TUM', 'SARS-CoV2']]}];
-
-  public selectedDataset = this.datasetItems[0];
-
+  public datasetItems: Array<{ id: string, label: string, datasets: string, data: Array<[string, string]> }> = [
+    {
+      id: 'All (TUM & Krogan)',
+      label: 'All',
+      datasets: 'TUM & Krogan',
+      data: [['TUM', 'HCoV'], ['TUM', 'SARS-CoV2'], ['Krogan', 'SARS-CoV2']]
+    },
+    {id: 'HCoV (TUM)', label: 'HCoV', datasets: 'TUM', data: [['TUM', 'HCoV']]},
+    {id: 'CoV2 (TUM & Krogan)', label: 'CoV2', datasets: 'TUM & Krogan', data: [['TUM', 'SARS-CoV2'], ['Krogan', 'SARS-CoV2']]},
+    {id: 'CoV2 (Krogan)', label: 'CoV2', datasets: 'Krogan', data: [['Krogan', 'SARS-CoV2']]},
+    {id: 'CoV2 (TUM)', label: 'CoV2', datasets: 'TUM', data: [['TUM', 'SARS-CoV2']]}];
+
+  public selectedDataset = this.datasetItems[3];
 
   @ViewChild('network', {static: false}) networkEl: ElementRef;
 
@@ -132,8 +136,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
 
   async ngAfterViewInit() {
     if (!this.network) {
-      this.selectedDataset = this.datasetItems[4];
-      await this.createNetwork(this.datasetItems[4].data);
+      this.selectedDataset = this.datasetItems[3];
+      await this.createNetwork(this.selectedDataset.data);
     }
   }
 
-- 
GitLab