Skip to content
Snippets Groups Projects
Select Git revision
  • 6d1cc041a83fc765b483078d3b3963e35f2b070c
  • master default protected
2 results

calculate_ani.py

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    analysis-panel.component.ts 20.57 KiB
    import {
      AfterViewInit,
      Component,
      ElementRef,
      EventEmitter,
      Input,
      OnChanges,
      OnInit,
      Output,
      SimpleChanges,
      ViewChild,
    } from '@angular/core';
    import {HttpClient} from '@angular/common/http';
    import {environment} from '../../../environments/environment';
    import {algorithmNames, AnalysisService} from '../../services/analysis/analysis.service';
    import {
      Drug,
      EdgeType,
      NodeAttributeMap,
      getDrugNodeId,
      getProteinNodeId,
      getWrapperFromNode,
      LegendContext,
      Node,
      Task,
      Tissue,
      Wrapper,
      NodeInteraction,
    } from '../../interfaces';
    import domtoimage from 'dom-to-image';
    import {NetworkSettings} from '../../network-settings';
    import {NetexControllerService} from 'src/app/services/netex-controller/netex-controller.service';
    import {defaultConfig, IConfig} from 'src/app/config';
    import {mapCustomEdge, mapCustomNode} from 'src/app/main-network';
    import {downLoadFile, pieChartContextRenderer, removeDuplicateObjectsFromList} from 'src/app/utils';
    import {DrugstoneConfigService} from 'src/app/services/drugstone-config/drugstone-config.service';
    import {NetworkHandlerService} from 'src/app/services/network-handler/network-handler.service';
    import {LegendService} from 'src/app/services/legend-service/legend-service.service';
    import { LoadingScreenService } from 'src/app/services/loading-screen/loading-screen.service';
    
    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)
    }
    
    interface Seeded {
      isSeed: boolean;
    }
    
    
    @Component({
      selector: 'app-analysis-panel',
      templateUrl: './analysis-panel.component.html',
      styleUrls: ['./analysis-panel.component.scss'],
    })
    export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit {
    
      @ViewChild('networkWithLegend', {static: false}) networkWithLegendEl: ElementRef;
      @Input() token: string | null = null;
    
    
      @Output() tokenChange = new EventEmitter<string | null>();
      @Output() showDetailsChange = new EventEmitter<Wrapper>();
      @Output() setInputNetwork = new EventEmitter<any>();
      @Output() visibleItems = new EventEmitter<[any[], [Node[], Tissue], NodeInteraction[]]>();
      public task: Task | null = null;
      public result: any = null;
    
      public fullscreen = false;
    
      public network: any;
      public nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
      // private drugNodes: any[] = [];
      // private drugEdges: any[] = [];
      public tab: 'meta' | 'network' | 'table' = 'table';
    
      // public adjacentDrugs = false;
      // public adjacentDrugList: Node[] = [];
      // public adjacentDrugEdgesList: Node[] = [];
      //
      // public adjacentDisordersProtein = false;
      // public adjacentDisordersDrug = false;
      //
      // public adjacentProteinDisorderList: Node[] = [];
      // public adjacentProteinDisorderEdgesList: Node[] = [];
      //
      // public adjacentDrugDisorderList: Node[] = [];
      // public adjacentDrugDisorderEdgesList: Node[] = [];
    
      private proteins: any;
      public effects: any;
    
      public tableDrugs: Array<Drug & Scored> = [];
      public tableProteins: Array<Node & Scored & Seeded> = [];
      public tableSelectedProteins: Array<Node & Scored & Seeded> = [];
      public tableNormalize = false;
      public tableHasScores = false;
    
      public LegendContext: LegendContext = 'drugTarget';
    
      public expressionExpanded = false;
      public selectedTissue: Tissue | null = null;
    
      public algorithmNames = algorithmNames;
    
      public tableDrugScoreTooltip = '';
      public tableProteinScoreTooltip = '';
    
      public expressionMap: NodeAttributeMap;
    
      public loading = false;
    
      constructor(public legendService: LegendService, public networkHandler: NetworkHandlerService, public drugstoneConfig: DrugstoneConfigService, private http: HttpClient, public analysis: AnalysisService, public netex: NetexControllerService, public loadingScreen: LoadingScreenService) {
      }
    
      async ngOnInit() {
      }
    
      ngAfterViewInit() {
        this.networkHandler.setActiveNetwork('analysis');
      }
    
      async ngOnChanges(changes: SimpleChanges) {
        await this.refresh();
      }
    
      private async refresh() {
        if (this.token) {
          this.loadingScreen.stateUpdate(true);
          this.task = await this.getTask(this.token);
          this.analysis.switchSelection(this.token);
    
          if (this.task.info.algorithm === 'degree') {
            this.tableDrugScoreTooltip =
              'Normalized number of direct interactions of the drug with the seeds. ' +
              'The higher the score, the more relevant the drug.';
            this.tableProteinScoreTooltip =
              'Normalized number of direct interactions of the protein with the seeds. ' +
              'The higher the score, the more relevant the protein.';
          } else if (this.task.info.algorithm === 'closeness' || this.task.info.algorithm === 'quick' || this.task.info.algorithm === 'super') {
            this.tableDrugScoreTooltip =
              'Normalized inverse mean distance of the drug to the seeds. ' +
              'The higher the score, the more relevant the drug.';
            this.tableProteinScoreTooltip =
              'Normalized inverse mean distance of the protein to the seeds. ' +
              'The higher the score, the more relevant the protein.';
          } else if (this.task.info.algorithm === 'trustrank') {
            this.tableDrugScoreTooltip =
              'Amount of ‘trust’ on the drug at termination of the algorithm. ' +
              'The higher the score, the more relevant the drug.';
            this.tableProteinScoreTooltip =
              'Amount of ‘trust’ on the protein at termination of the algorithm. ' +
              'The higher the score, the more relevant the protein.';
          } else if (this.task.info.algorithm === 'proximity') {
            this.tableDrugScoreTooltip =
              'Empirical z-score of mean minimum distance between the drug’s targets and the seeds. ' +
              'The lower the score, the more relevant the drug.';
            this.tableProteinScoreTooltip =
              'Empirical z-score of mean minimum distance between the drug’s targets and the seeds. ' +
              'The lower the score, the more relevant the drug.';
          }
    
          if (this.task && this.task.info.done) {
            this.loading = true;
            this.netex.getTaskResult(this.token).then(result => {
              this.drugstoneConfig.set_analysisConfig(result.parameters.config);
              this.result = result;
              if (this.result.parameters.target === 'drug') {
                this.legendService.add_to_context('drug');
              } else {
                this.legendService.add_to_context('drugTarget');
              }
              const nodeAttributes = this.result.nodeAttributes || {};
    
              this.networkHandler.activeNetwork.seedMap = nodeAttributes.isSeed || {};
    
              // Reset
              this.nodeData = {nodes: null, edges: null};
              this.networkHandler.activeNetwork.networkEl.nativeElement.innerHTML = '';
              this.networkHandler.activeNetwork.networkInternal = null;
              // Create
              this.createNetwork(this.result).then(nw => {
                const nodes = nw.nodes;
                const edges = nw.edges;
    
                this.networkHandler.activeNetwork.inputNetwork = {nodes: nodes, edges: edges};
                this.nodeData.nodes = new vis.DataSet(nodes);
                this.nodeData.edges = new vis.DataSet(edges);
                const container = this.networkHandler.activeNetwork.networkEl.nativeElement;
                const isBig = nodes.length > 100 || edges.length > 100;
                const options = NetworkSettings.getOptions(isBig ? 'analysis-big' : 'analysis', this.drugstoneConfig.currentConfig());
                // @ts-ignore
                options.groups = this.drugstoneConfig.currentConfig().nodeGroups;
                // @ts-ignore
                for (const g of Object.values(options.groups)) {
                  // @ts-ignore
                  delete g.renderer;
                }
                this.drugstoneConfig.config.physicsOn = !isBig;
                this.networkHandler.activeNetwork.networkInternal = new vis.Network(container, this.nodeData, options);
                this.networkHandler.activeNetwork.networkInternal.on('stabilizationIterationsDone', () => {
                  if (!this.drugstoneConfig.config.physicsOn) {
                    this.networkHandler.activeNetwork.updatePhysicsEnabled(false);
                  }
                  this.networkHandler.updateAdjacentNodes();
                });
                this.tableDrugs = nodes.filter(e => e.drugstoneId && e.drugstoneType === 'drug');
                this.tableDrugs.forEach((r) => {
                  r.rawScore = r.score;
                });
                this.tableProteins = nodes.filter(e => e.drugstoneId && e.drugstoneType === 'protein');
                this.tableSelectedProteins = [];
                this.tableProteins.forEach((r) => {
                  r.rawScore = r.score;
                  r.isSeed = this.networkHandler.activeNetwork.seedMap[r.id];
                  const wrapper = getWrapperFromNode(r);
                  if (this.analysis.inSelection(wrapper)) {
                    this.tableSelectedProteins.push(r);
                  }
                });
    
    
                this.tableHasScores = ['trustrank', 'closeness', 'degree', 'betweenness', 'quick', 'super']
                  .indexOf(this.task.info.algorithm) !== -1;
                if (this.tableHasScores) {
                  this.toggleNormalization(true);
                }
                this.networkHandler.activeNetwork.networkInternal.setData({nodes: undefined, edge: undefined});
                setTimeout(() => {
                  this.networkHandler.activeNetwork.networkInternal.setData(this.nodeData);
                }, 1000);
    
                this.networkHandler.activeNetwork.networkInternal.on('deselectNode', (properties) => {
                  this.showDetailsChange.emit(null);
                });
    
                this.networkHandler.activeNetwork.networkInternal.on('doubleClick', (properties) => {
                  const nodeIds: Array<string> = properties.nodes;
                  if (nodeIds.length > 0) {
                    const nodeId = nodeIds[0];
                    const node = this.nodeData.nodes.get(nodeId);
                    if (node.drugstoneId === undefined || node.nodeType === 'drug' || node.drugstoneType !== 'protein') {
                      this.analysis.unmappedNodeToast();
                      return;
                    }
                    const wrapper = getWrapperFromNode(node);
                    if (this.analysis.inSelection(wrapper)) {
                      this.analysis.removeItems([wrapper]);
                      this.analysis.getCount();
                    } else {
                      this.analysis.addItems([wrapper]);
                      this.analysis.getCount();
                    }
                  }
                });
    
                this.networkHandler.activeNetwork.networkInternal.on('click', (properties) => {
                  const selectedNodes = this.nodeData.nodes.get(properties.nodes);
                  if (selectedNodes.length > 0) {
                    this.showDetailsChange.emit(getWrapperFromNode(selectedNodes[0]));
                  } else {
                    this.showDetailsChange.emit(null);
                  }
                });
    
                this.analysis.subscribeList((items, selected) => {
                  // return if analysis panel is closed or no nodes are loaded
                  if (!this.token) {
                    return;
                  }
    
                  if (selected !== null) {
                    const updatedNodes: Node[] = [];
                    for (const item of items) {
                      const node = this.nodeData.nodes.get(item.id);
                      if (!node) {
                        continue;
                      }
                      const pos = this.networkHandler.activeNetwork.networkInternal.getPositions([item.id]);
                      node.x = pos[item.id].x;
                      node.y = pos[item.id].y;
                      const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false;
                      const nodeStyled = NetworkSettings.getNodeStyle(
                        node,
                        this.drugstoneConfig.currentConfig(),
                        isSeed,
                        selected,
                        this.networkHandler.activeNetwork.getGradient(item.id),
                        this.networkHandler.activeNetwork.nodeRenderer
                      );
                      updatedNodes.push(nodeStyled);
                    }
                    this.nodeData.nodes.update(updatedNodes);
    
                    const proteinSelection = this.tableSelectedProteins;
                    for (const item of items) {
                      // TODO: Refactor!
                      const found = proteinSelection.findIndex((i) => getProteinNodeId(i) === item.id);
                      const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.id);
                      if (selected && found === -1 && tableItem) {
                        proteinSelection.push(tableItem);
                      }
                      if (!selected && found !== -1 && tableItem) {
                        proteinSelection.splice(found, 1);
                      }
                    }
                    this.tableSelectedProteins = [...proteinSelection];
                  } else {
                    // else: selected is null
                    const updatedNodes = [];
                    this.nodeData.nodes.forEach((node) => {
                      const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false;
                      if (!isSeed) {
                        return;
                      }
                      const nodeStyled = NetworkSettings.getNodeStyle(
                        node,
                        this.drugstoneConfig.currentConfig(),
                        isSeed,
                        selected,
                        this.networkHandler.activeNetwork.getGradient(node.id),
                        this.networkHandler.activeNetwork.nodeRenderer
                      );
                      updatedNodes.push(nodeStyled);
                    });
                    this.nodeData.nodes.update(updatedNodes);
    
                    const proteinSelection = [];
                    for (const item of items) {
                      const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.id);
                      if (tableItem) {
                        proteinSelection.push(tableItem);
                      }
                    }
                    this.tableSelectedProteins = [...proteinSelection];
                  }
                });
                this.emitVisibleItems(true);
              });
            this.loadingScreen.stateUpdate(false);
    
            });
          }
        }
    
    
      }
    
      public emitVisibleItems(on: boolean) {
        if (on) {
          this.visibleItems.emit([this.nodeData.nodes, [this.proteins, this.selectedTissue], this.nodeData.edges]);
        } else {
          this.visibleItems.emit(null);
        }
      }
    
      private async getTask(token: string): Promise<any> {
        return await this.http.get(`${this.netex.getBackend()}task/?token=${token}`).toPromise();
      }
    
      close() {
        this.networkHandler.activeNetwork.gradientMap = {};
        this.drugstoneConfig.remove_analysisConfig();
        this.expressionExpanded = false;
        this.expressionMap = undefined;
        this.networkHandler.activeNetwork.seedMap = {};
        this.networkHandler.activeNetwork.highlightSeeds = false;
        this.analysis.switchSelection('main');
        this.token = null;
        this.tokenChange.emit(this.token);
        this.legendService.remove_from_context('drug');
        this.legendService.remove_from_context('drugTarget');
        this.emitVisibleItems(false);
      }
    
      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.tableProteins);
          if (this.task.info.target === 'drug') {
            normalizeFn(this.tableDrugs);
          }
        } else {
          unnormalizeFn(this.tableProteins);
          if (this.task.info.target === 'drug') {
            unnormalizeFn(this.tableDrugs);
          }
        }
      }
    
      public downloadLink(view: string): string {
        return `${this.netex.getBackend()}task_result/?token=${this.token}&view=${view}&fmt=csv`;
      }
    
      /**
       * Maps analysis result returned from database to valid Vis.js network input
       *
       * @param result
       * @returns
       */
      public async createNetwork(result: any): Promise<{ edges: any[]; nodes: any[]; }> {
        const identifier = this.drugstoneConfig.currentConfig().identifier;
    
        // add drugGroup and foundNodesGroup for added nodes
        // these groups can be overwritten by the user
        const nodes = [];
        let edges = [];
    
        const attributes = result.nodeAttributes || {};
    
        this.proteins = [];
        this.effects = [];
        const network = result.network;
        network.nodes = [...new Set<string>(network.nodes)];
    
        const details = attributes.details || {};
        const nodeIdMap = {};
        // @ts-ignore
        Object.entries(details).filter(e => e[1].drugstoneType === 'protein').forEach(e => {
          // @ts-ignore
          e[1].drugstoneId.forEach(id => {
            nodeIdMap[id] = e[1][identifier][0];
          });
        });
        for (const nodeId of network.nodes) {
          if (details[nodeId]) {
            const nodeDetails = details[nodeId];
            nodeDetails.id = nodeDetails.id ? nodeDetails.id : (typeof nodeDetails.drugstoneId === 'string' ? nodeDetails.drugstoneId : nodeDetails.drugstoneId[0]);
            if (nodeDetails.drugstoneId && nodeDetails.drugstoneType === 'protein') {
              // node is protein from database, has been mapped on init to backend protein from backend
              // or was found during analysis
              // FIXME connectorNodes are not visualized correctly
              nodeDetails.group = result.targetNodes && result.targetNodes.indexOf(nodeId) !== -1 ? 'foundNode' : (nodeDetails.group ? nodeDetails.group : 'connectorNode');
              nodeDetails.label = nodeDetails.label ? nodeDetails.label : nodeDetails[identifier];
              nodeDetails.id = nodeDetails[identifier][0] ? nodeDetails[identifier][0] : nodeDetails.id;
              this.proteins.push(nodeDetails);
            } else if (nodeDetails.drugstoneId && nodeDetails.drugstoneType === 'drug') {
              // node is drug, was found during analysis
              nodeDetails.type = 'Drug';
              nodeDetails.group = 'foundDrug';
            } else {
              // node is custom input from user, could not be mapped to backend protein
              nodeDetails.group = nodeDetails.group ? nodeDetails.group : 'default';
              nodeDetails.label = nodeDetails.label ? nodeDetails.label : nodeDetails[identifier];
            }
            // further analysis and the button function can be used to highlight seeds
            // option to use scores[node] as gradient, but sccores are very small
            nodes.push(NetworkSettings.getNodeStyle(nodeDetails as Node, this.drugstoneConfig.currentConfig(), false, false, 1, this.networkHandler.activeNetwork.nodeRenderer));
          } else {
            console.log('Missing details for ' + nodeId);
          }
        }
        const uniqEdges = [];
        for (const edge of network.edges) {
          const e = mapCustomEdge(edge, this.drugstoneConfig.currentConfig());
          e.from = e.from[0] === 'p' && nodeIdMap[e.from] ? nodeIdMap[e.from] : e.from;
          e.to = e.to[0] === 'p' && nodeIdMap[e.to] ? nodeIdMap[e.to] : e.to;
          const hash = e.from + '_' + e.to;
          if (uniqEdges.indexOf(hash) === -1) {
            uniqEdges.push(hash);
            edges.push(e);
          }
        }
        // remove self-edges/loops
        if (!this.drugstoneConfig.currentConfig().selfReferences) {
          edges = edges.filter(el => el.from !== el.to);
        }
        return {
          nodes,
          edges,
        };
      }
    
      getResultNodes() {
        if (this.nodeData && this.nodeData['nodes']) {
          return this.nodeData['nodes'].get();
        }
        return [];
      }
    
      getResultEdges() {
        if (this.nodeData && this.nodeData['edges']) {
          return this.nodeData['edges'].get().filter(e => !e.id || !e.groupName || (typeof e.from === 'string' && typeof e.to === 'string'));
        }
        return [];
      }
    
      public tableProteinSelection = (e): void => {
        const oldSelection = [...this.tableSelectedProteins];
        this.tableSelectedProteins = e;
        const addItems = [];
        const removeItems = [];
        for (const i of this.tableSelectedProteins) {
          const wrapper = getWrapperFromNode(i);
          if (oldSelection.indexOf(i) === -1) {
            addItems.push(wrapper);
          }
        }
        for (const i of oldSelection) {
          const wrapper = getWrapperFromNode(i);
          if (this.tableSelectedProteins.indexOf(i) === -1) {
            removeItems.push(wrapper);
          }
        }
        this.analysis.addItems(addItems);
        this.analysis.removeItems(removeItems);
      };
    
      public toggleFullscreen() {
        this.fullscreen = !this.fullscreen;
        this.loadingScreen.fullscreenUpdate(this.fullscreen)
      }
    }