Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
analysis-window.component.ts 12.52 KiB
import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges,
  OnInit,
  Output,
  SimpleChanges,
  ViewChild
} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {environment} from '../../../environments/environment';
import {AnalysisService, algorithmNames} from '../../analysis.service';
import {
  Protein, Task, ViralProtein, Drug, Wrapper, WrapperType,
  getWrapperFromProtein, getWrapperFromDrug, getWrapperFromViralProtein, getNodeIdsFromPDI, getNodeIdsFromPPI
} from '../../interfaces';
import html2canvas from 'html2canvas';
import {toast} from 'bulma-toast';
import {NetworkSettings} from '../../network-settings';

declare var vis: any;

interface Scored {
  score: number;  // Normalized or unnormalized (whichever user selects, will be displayed in the table)
  rawScore: number;  // Unnormalized (kept to restore unnormalized value)
}

@Component({
  selector: 'app-analysis-window',
  templateUrl: './analysis-window.component.html',
  styleUrls: ['./analysis-window.component.scss'],
})
export class AnalysisWindowComponent implements OnInit, OnChanges {

  @ViewChild('network', {static: false}) networkEl: ElementRef;

  @Input() token: string | null = null;

  @Output() tokenChange = new EventEmitter<string | null>();
  @Output() showDetailsChange = new EventEmitter<Wrapper>();
  @Output() visibleItems = new EventEmitter<any>();

  public task: Task | null = null;
  public indexscreenshot = 1;

  private network: any;
  private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
  private drugNodes: any[] = [];
  private drugEdges: any[] = [];
  public showDrugs = false;
  public tab = 'network';
  public physicsEnabled = true;
  public drugstatus = true;

  private proteins: any;
  public effects: any;

  public tableDrugs: Array<Drug & Scored> = [];
  public tableProteins: Array<Protein & Scored> = [];
  public tableViralProteins: Array<ViralProtein & Scored> = [];
  public tableNormalize = false;
  public tableHasScores = false;

  public algorithmNames = algorithmNames;

  constructor(private http: HttpClient, public analysis: AnalysisService) {
  }
  async ngOnInit() {
  }

  async ngOnChanges(changes: SimpleChanges) {
    await this.refresh();
  }

  private async refresh() {
    if (this.token) {
      this.task = await this.getTask(this.token);

      if (this.task && this.task.info.done) {
        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;

        // 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 = NetworkSettings.getOptions('analysis');

        this.network = new vis.Network(container, this.nodeData, options);

        const promises: Promise<any>[] = [];
        promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=proteins`).toPromise()
          .then((table) => {
            this.tableProteins = table;
            this.tableProteins.forEach((r) => r.rawScore = r.score);
          }));
        promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=viral_proteins`).toPromise()
          .then((table) => {
            this.tableViralProteins = table;
            this.tableViralProteins.forEach((r) => r.rawScore = r.score);
          }));
        promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=drugs`).toPromise()
          .then((table) => {
            this.tableDrugs = table;
            this.tableDrugs.forEach((r) => r.rawScore = r.score);
          }));
        await Promise.all(promises);

        this.tableHasScores = ['trustrank', 'closeness', 'degree', 'quick'].indexOf(this.task.info.algorithm) !== -1;
        if (this.tableHasScores) {
          this.toggleNormalization(true);
        }

        this.network.on('deselectNode', (properties) => {
          this.showDetailsChange.emit(null);
        });

        this.network.on('click', (properties) => {
          const selectedNodes = this.nodeData.nodes.get(properties.nodes);
          if (selectedNodes.length > 0) {
            const selectedNode = selectedNodes[0];
            const wrapper = selectedNode.wrapper;

            if (properties.event.srcEvent.ctrlKey) {
              if (this.analysis.inSelection(wrapper)) {
                this.analysis.removeItem(wrapper);
              } else {
                this.analysis.addItem(wrapper);
                this.analysis.getCount();
              }
            }
            this.showDetailsChange.emit(wrapper);
          } else {
            this.showDetailsChange.emit(null);
          }
        });

        this.analysis.subscribe((item, selected) => {
          const node = this.nodeData.nodes.get(item.nodeId);
          if (!node) {
            return;
          }
          const pos = this.network.getPositions([item.nodeId]);
          node.x = pos[item.nodeId].x;
          node.y = pos[item.nodeId].y;
          Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, node.isSeed, selected));
          this.nodeData.nodes.update(node);
        });
      }
    }
    this.emitVisibleItems(true);
  }

  public emitVisibleItems(on: boolean) {
    if (on) {
      this.visibleItems.emit([this.nodeData.nodes, [this.proteins, this.effects]]);
    } else {
      this.visibleItems.emit(null);
    }
  }

  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);
    this.emitVisibleItems(false);
  }

  export() {

  }

  public toggleNormalization(normalize: boolean) {
    this.tableNormalize = normalize;

    const normalizeFn = (table) => {
      let max = 0;
      table.forEach(i => {
        if (i.rawScore > max) {
          max = i.rawScore;
        }
      });
      table.forEach(i => {
        i.score = i.rawScore / max;
      });
    };

    const unnormalizeFn = (table) => {
      table.forEach(i => {
        i.score = i.rawScore;
      });
    };

    if (normalize) {
      normalizeFn(this.tableDrugs);
      normalizeFn(this.tableProteins);
      normalizeFn(this.tableViralProteins);
    } else {
      unnormalizeFn(this.tableDrugs);
      unnormalizeFn(this.tableProteins);
      unnormalizeFn(this.tableViralProteins);
    }
  }

  public downloadLink(view: string): string {
    return `${environment.backend}task_result/?token=${this.token}&view=${view}&fmt=csv`;
  }

  public inferNodeType(nodeId: string): WrapperType {
    if (nodeId.indexOf('-') !== -1 || nodeId.indexOf('_') !== -1) {
      return 'virus';
    } else if (nodeId.startsWith('DB')) {
      return 'drug';
    }
    return 'host';
  }

  public createNetwork(result: any): { edges: any[], nodes: any[] } {
    const nodes = [];
    const edges = [];

    const attributes = result.nodeAttributes || {};

    this.proteins = [];
    this.effects = [];
    const network = result.network;

    const nodeTypes = attributes.nodeTypes || {};
    const isSeed = attributes.isSeed || {};
    const scores = attributes.scores || {};
    const details = attributes.details || {};
    const wrappers: {[key: string]: Wrapper}  = {};
    for (const node of network.nodes) {
      if (nodeTypes[node] === 'host') {
        this.proteins.push(details[node]);
        wrappers[node] = getWrapperFromProtein(details[node]);
      } else if (nodeTypes[node] === 'virus') {
        this.effects.push(details[node]);
        wrappers[node] = getWrapperFromViralProtein(details[node]);
      } else if (nodeTypes[node] === 'drug') {
        wrappers[node] = getWrapperFromDrug(details[node]);
      }
      nodes.push(this.mapNode(this.inferNodeType(node), details[node], isSeed[node], scores[node]));
    }

    for (const edge of network.edges) {
      edges.push(this.mapEdge(edge, 'protein-protein', wrappers));
    }

    for (const edge of network.edges) {
      edges.push(this.mapEdge(edge, 'protein-protein', wrappers));
    }


    return {
      nodes,
      edges,
    };
  }

  private mapNode(nodeType: WrapperType, details: Protein | ViralProtein | Drug, isSeed?: boolean, score?: number): any {
    let nodeLabel;
    let wrapper: Wrapper;
    if (nodeType === 'host') {
      const protein = details as Protein;
      wrapper = getWrapperFromProtein(protein);
      nodeLabel = protein.name;
      if (!protein.name) {
        nodeLabel = protein.proteinAc;
      }
    } else if (nodeType === 'drug') {
      const drug = details as Drug;
      wrapper = getWrapperFromDrug(drug);
      nodeLabel = drug.name;
    } else if (nodeType === 'virus') {
      const viralProtein = details as ViralProtein;
      wrapper = getWrapperFromViralProtein(viralProtein);
      nodeLabel = viralProtein.effectName;
    }

    const node = NetworkSettings.getNodeStyle(nodeType, isSeed, this.analysis.inSelection(wrapper));

    node.id = wrapper.nodeId;
    node.label = nodeLabel;
    node.nodeType = nodeType;
    node.isSeed = isSeed;
    node.wrapper = wrapper;
    return node;
  }

  private mapEdge(edge: any, type: 'protein-protein' | 'to-drug', wrappers?: {[key: string]: Wrapper}): any {
    let edgeColor;
    if (type === 'protein-protein') {
      edgeColor = {
        color: NetworkSettings.getColor('edgeHostVirus'),
        highlight: NetworkSettings.getColor('edgeHostVirusHighlight'),
      };
      const {from, to} = getNodeIdsFromPPI(edge, wrappers);
      return {
        from, to,
        color: edgeColor,
      };
    } else if (type === 'to-drug') {
      edgeColor = {
        color: NetworkSettings.getColor('edgeHostDrug'),
        highlight: NetworkSettings.getColor('edgeHostDrugHighlight'),
      };
      const {from, to} = getNodeIdsFromPDI(edge);
      return {
        from, to,
        color: edgeColor,
      };
    }
  }

  public async toggleDrugs(bool: boolean) {
    this.showDrugs = bool;
    this.nodeData.nodes.remove(this.drugNodes);
    this.nodeData.edges.remove(this.drugEdges);
    this.drugNodes = [];
    this.drugEdges = [];
    if (this.showDrugs) {
      const proteinAcs = this.proteins.map((protein) => protein.proteinAc);
      // tslint:disable-next-line:max-line-length
      const result = await this.http.get<any>(`${environment.backend}drug_interactions/?proteins=${JSON.stringify(proteinAcs)}`).toPromise().catch((err: HttpErrorResponse) => {
        // simple logging, but you can do a lot more, see below
        toast({
          message: 'An error occured while fetching the drugs.',
          duration: 5000,
          dismissible: true,
          pauseOnHover: true,
          type: 'is-danger',
          position: 'top-center',
          animate: {in: 'fadeIn', out: 'fadeOut'}
        });
        this.showDrugs = false;
        return;
      });

      const drugs = result.drugs;
      const edges = result.edges;

      if (drugs.length === 0) {
        toast({
          message: 'No drugs found.',
          duration: 5000,
          dismissible: true,
          pauseOnHover: true,
          type: 'is-warning',
          position: 'top-center',
          animate: {in: 'fadeIn', out: 'fadeOut'}
        });
      } else {
        for (const drug of drugs) {
          this.drugNodes.push(this.mapNode('drug', drug, false, null));
        }

        for (const interaction of edges) {
          const edge = {from: interaction.proteinAc, to: interaction.drugId};
          this.drugEdges.push(this.mapEdge(edge, 'to-drug'));
        }
        this.nodeData.nodes.add(Array.from(this.drugNodes.values()));
        this.nodeData.edges.add(Array.from(this.drugEdges.values()));
      }
    }
  }


  public updatePhysicsEnabled(bool: boolean) {
    this.physicsEnabled = bool;
    this.network.setOptions({
      physics: {
        enabled: this.physicsEnabled,
        stabilization: {
          enabled: false,
        },
      }
    });
  }

  public screenshot() {
    const elem = document.getElementById(this.indexscreenshot.toString());
    html2canvas(elem).then((canvas) => {
      const generatedImage1 = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
      const a = document.createElement('a');
      a.href = generatedImage1;
      a.download = `Resulting_Network.png`;
      a.click();

    });
  }

  public updateshowdrugs(bool) {
    this.drugstatus = bool;

  }

}