From 8ac991137769c9315fd4e304cb3725ad9619ec82 Mon Sep 17 00:00:00 2001
From: Maiykol <hartung.michael@outlook.com>
Date: Sun, 20 Jun 2021 17:53:44 +0200
Subject: [PATCH] support for ensg numbers; dealing with multiple ensg numbers
 refering to the same entrez ID

---
 .../analysis-panel.component.ts               | 21 +++++++++++---
 .../info-tile/info-tile.component.html        | 12 ++++++++
 .../info-tile/info-tile.component.ts          |  4 +++
 .../query-tile/query-tile.component.ts        |  2 +-
 src/app/interfaces.ts                         | 20 ++++++++++++-
 src/app/main-network.ts                       | 28 +++++++++++--------
 .../explorer-page/explorer-page.component.ts  |  6 ++--
 src/index.html                                | 21 +++++++-------
 8 files changed, 85 insertions(+), 29 deletions(-)

diff --git a/src/app/components/analysis-panel/analysis-panel.component.ts b/src/app/components/analysis-panel/analysis-panel.component.ts
index d353ebf5..8327f8a0 100644
--- a/src/app/components/analysis-panel/analysis-panel.component.ts
+++ b/src/app/components/analysis-panel/analysis-panel.component.ts
@@ -20,6 +20,7 @@ import {
   WrapperType,
   getWrapperFromNode,
   getWrapperFromDrug,
+  getWrapperFromCustom,
   getNodeIdsFromPDI,
   getNodeIdsFromPPI,
   getProteinNodeId, 
@@ -386,6 +387,13 @@ export class AnalysisPanelComponent implements OnInit, OnChanges {
     }
   }
 
+
+  /**
+   * Maps analysis result returned from database to valid Vis.js network input
+   * 
+   * @param result 
+   * @returns 
+   */
   public createNetwork(result: any): { edges: any[], nodes: any[] } {
     const config = result.parameters.config;
 
@@ -405,12 +413,19 @@ export class AnalysisPanelComponent implements OnInit, OnChanges {
     const wrappers: { [key: string]: Wrapper } = {};
     for (const node of network.nodes) {
       // backend converts object keys to PascalCase: p_123 --> p123
-      const nodeObjectKey = node.split('_').join('')
+      const nodeObjectKey = node.split('_').join('');
+      console.log(nodeObjectKey)
       if (nodeTypes[nodeObjectKey] === 'protein') {
+        // node is protein from database, has been mapped on init to backend protein from backend
+        // or was found during analysis
         this.proteins.push(details[nodeObjectKey]);
         wrappers[node] = getWrapperFromNode(details[nodeObjectKey]);
       } else if (nodeTypes[nodeObjectKey] === 'drug') {
+        // node is drug, was found during analysis
         wrappers[node] = getWrapperFromDrug(details[nodeObjectKey]);
+      } else {
+        // node is custom input from user, could not be mapped to backend protein
+        wrappers[node] = getWrapperFromCustom(details[nodeObjectKey]);
       }
       nodes.push(this.mapNode(config, wrappers[node], isSeed[nodeObjectKey], scores[nodeObjectKey]));
     }
@@ -425,15 +440,13 @@ export class AnalysisPanelComponent implements OnInit, OnChanges {
 
   private mapNode(config: IConfig, wrapper: Wrapper, isSeed?: boolean, score?: number): any {
     // const node = NetworkSettings.getNodeStyle(nodeType, isSeed, this.analysis.inSelection(wrapper));
-    console.log(wrapper)
     let group = this.inferNodeGroup(wrapper);
     if (typeof group === 'undefined' || typeof config.nodeGroups[group] === 'undefined') {
       group = 'default';
     }
-    console.log(group)
     const node = JSON.parse(JSON.stringify(config.nodeGroups[group]));
     node.id = wrapper.id;
-    node.label = wrapper.data.name;
+    node.label = wrapper.data.symbol;
     node.nodeType = group;
     node.isSeed = isSeed;
     node.wrapper = wrapper;
diff --git a/src/app/components/info-tile/info-tile.component.html b/src/app/components/info-tile/info-tile.component.html
index 8e6fbef5..73c8bf37 100644
--- a/src/app/components/info-tile/info-tile.component.html
+++ b/src/app/components/info-tile/info-tile.component.html
@@ -4,6 +4,10 @@
       <b><span>Name:</span></b>
       <span class="is-capitalized"> {{ wrapper.data.name }}</span>
     </p>
+    <p *ngIf="wrapper.data.symbol" [ngClass]="{'text-normal':smallStyle}">
+      <b><span>Symbol:</span></b>
+      <span class="is-capitalized"> {{ wrapper.data.symbol }}</span>
+    </p>
     <p *ngIf="wrapper.type" [ngClass]="{'text-normal':smallStyle}">
       <b><span>Type:</span></b>
       <span class="is-capitalized"> {{ wrapper.type }}</span>
@@ -24,6 +28,14 @@
         <span class="is-capitalized"> {{ wrapper.data.uniprotAc }}</span>
       </a>
     </p>
+    <p *ngIf="wrapper.data.ensg" [ngClass]="{'text-normal':smallStyle}">
+      <b><span>Ensembl:</span></b>
+      <a 
+        *ngFor="let ensg of wrapper.data.ensg"
+        href="https://www.ensembl.org/Homo_sapiens/Gene/Summary?g={{ ensg }}" target="_blank">
+        <span class="is-capitalized"> {{ ensg }}</span>
+      </a>
+    </p>
 
   </div>
 
diff --git a/src/app/components/info-tile/info-tile.component.ts b/src/app/components/info-tile/info-tile.component.ts
index 44bd205e..aff398c2 100644
--- a/src/app/components/info-tile/info-tile.component.ts
+++ b/src/app/components/info-tile/info-tile.component.ts
@@ -18,6 +18,10 @@ export class InfoTileComponent implements OnInit {
   ngOnInit(): void {
   }
 
+  public print(x) {
+    console.log(x)
+  }
+
   public beautify(url: string): string {
     if (url.startsWith('https://')) {
       url = url.substr('https://'.length);
diff --git a/src/app/components/query-tile/query-tile.component.ts b/src/app/components/query-tile/query-tile.component.ts
index effd4a12..9dfbdaa7 100644
--- a/src/app/components/query-tile/query-tile.component.ts
+++ b/src/app/components/query-tile/query-tile.component.ts
@@ -15,7 +15,7 @@ export class QueryTileComponent {
   querySearch(term: string, item: Wrapper) {
     term = term.toLowerCase();
     const data = item.data as Node;
-    return data.name.toLowerCase().indexOf(term) > -1 || item.type.toLowerCase().indexOf(term) > -1;
+    return data.symbol.toLowerCase().indexOf(term) > -1 || item.type.toLowerCase().indexOf(term) > -1;
   }
 
   select(item) {
diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts
index ed9d700d..56f66073 100644
--- a/src/app/interfaces.ts
+++ b/src/app/interfaces.ts
@@ -2,9 +2,11 @@ import {AlgorithmType, QuickAlgorithmType} from './services/analysis/analysis.se
 
 export interface Node {
   name: string;
+  symbol: string;
   id: string;
   netexId?: string;
   uniprotAc?: string;
+  ensg?: Array<string>;
   group?: string;
   color?: string;
   shape?: string;
@@ -115,6 +117,17 @@ export function getId(gene: Node) {
   return `${gene.id}`;
 }
 
+export function getWrapperFromCustom(gene: Node): Wrapper {
+  /**
+   * Constructs wrapper interface for gene
+   */
+  return {
+    id: getNodeId(gene),
+    nodeId: getNodeId(gene),
+    type: 'custom',
+    data: gene,
+  };
+}
 
 export function getWrapperFromNode(gene: Node): Wrapper {
   /**
@@ -138,7 +151,10 @@ export function getWrapperFromDrug(drug: Drug): Wrapper {
   };
 }
 
-export type WrapperType = 'protein' | 'drug';
+// protein = node that exists as protein in backend
+// drug = drug from backend, found in analysis
+// custom = custom node from user that could not be mapped to protein
+export type WrapperType = 'protein' | 'drug' | 'custom';
 export type EdgeType = 'protein-protein' | 'protein-drug';
 
 export interface Wrapper {
@@ -148,7 +164,9 @@ export interface Wrapper {
   data: {
     id: string;
     name: string;
+    symbol?: string;
     netexId?: string;
+    ensg?: Array<string>;
     shape?: string;
     color?: string;
     interactions?: any;
diff --git a/src/app/main-network.ts b/src/app/main-network.ts
index 1d827e18..63d349fb 100644
--- a/src/app/main-network.ts
+++ b/src/app/main-network.ts
@@ -51,7 +51,17 @@ export class ProteinNetwork {
     if (typeof group === 'undefined' || typeof config.nodeGroups[group] === 'undefined') {
       group = 'default';
     }
-    const node = JSON.parse(JSON.stringify(config.nodeGroups[group]));
+    let node = JSON.parse(JSON.stringify(config.nodeGroups[group]));
+
+    // node.name is actually group name since it comes from the group configuration
+    // this property is already stored in the wrapper object
+    // instead, node.name should reflect the actual node name
+    // "node.name = customNode.name";
+    // update the node with custom node properties, including values fetched from backend
+    node = {
+      ... node,
+      ... customNode
+    }
 
     // label is only used for network visualization
     let nodeLabel = customNode.name;
@@ -59,20 +69,16 @@ export class ProteinNetwork {
       nodeLabel = customNode.userId;
     }
 
-    // node.name is actually group name since it comes from the group configuration
-    // this property is already stored in the wrapper object
-    // instead, node.name should reflect the actual node name
-    node.name = customNode.name;
-
     if (node.image) {
       node.shape = 'image';
     }
     node.label = nodeLabel;
-    node.id = customNode.id;
-    node.x = customNode.x;
-    node.y = customNode.y;
-    node.uniprotAc = customNode.uniprotAc;
-    node.netexId = customNode.netexId;
+    // node.id = customNode.id;
+    // node.x = customNode.x;
+    // node.y = customNode.y;
+    // node.uniprotAc = customNode.uniprotAc;
+    // node.netexId = customNode.netexId;
+    // node.ensg = customNode.ensg;
     // console.log(node)
     return node;
   }
diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts
index 4dbbdb5c..81f1d903 100644
--- a/src/app/pages/explorer-page/explorer-page.component.ts
+++ b/src/app/pages/explorer-page/explorer-page.component.ts
@@ -74,7 +74,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
           document.getElementById('main-column').classList.add('rightgone');
         }
       }
-
+      console.log(key)
       this.myConfig[key] = configObj[key];
     }
   }
@@ -193,7 +193,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
   }
 
   async ngAfterViewInit() {
-    this.createNetwork();
+    // TODO find out if this had a function? we were loading the network twice
+    // this.createNetwork();
 
     if (this.onload) {
       // tslint:disable-next-line:no-eval
@@ -211,6 +212,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
   }
 
   private async getNetwork() {
+
     const network = JSON.parse(this.networkJSON);
 
     // map data to nodes in backend
diff --git a/src/index.html b/src/index.html
index 30c069b4..65f8f542 100644
--- a/src/index.html
+++ b/src/index.html
@@ -39,12 +39,18 @@
   <network-expander id="netexp1"
                     config='{
                       "nodeGroups": {"0.5": {"type": "gene", "color": "rgb(204, 255, 51)", "name": "0.5", "shape": "circle"}, "1.5": {"type": "gene", "color": "rgb(102, 255, 51)", "name": "1.5", "shape": "circle"}, "2.0": {"type": "gene", "color": "rgb(51, 204, 51)", "name": "2.0", "shape": "circle"}, "-2.0": {"type": "gene", "color": "rgb(255, 0, 0)", "name": "-2.0", "shape": "circle"}},
-                      "edgeGroups": {"custom": {"color": "black", "name": "Custom Group"}}, "idientifier": "hugo",
+                      "edgeGroups": {"custom": {"color": "black", "name": "Custom Group"}}, 
+                      "identifier": "ensg",
                       "legendUrl": "https://exbio.wzw.tum.de/covex/assets/leg1.png"
                     }'
-                    network= '{"nodes": [{"Name": "SFTPD", "d": 2.1770573418095793, "color": "rgb(51, 204, 51)", "type": "square", "label": "SFTPD", "x": -0.31956085006193347, "y": -0.7406466643934345, "size": 10, "id": "SFTPD", "group": 1.5, "name": "SFTPD"}, {"Name": "FGG", "d": 3.0919984753400342, "color": "rgb(51, 204, 51)", "type": "square", "label": "FGG", "x": 0.07173702374771213, "y": -0.5272230319152038, "size": 10, "id": "FGG", "group": 1.5, "name": "FGG"}, {"Name": "FGB", "d": 1.7586794205006901, "color": "rgb(51, 204, 51)", "type": "square", "label": "FGB", "x": 0.1862980578421022, "y": -0.5379861465902673, "size": 10, "id": "FGB", "group": 1.5, "name": "FGB"}, {"Name": "CFI", "d": 1.6225326254583097, "color": "rgb(51, 204, 51)", "type": "square", "label": "CFI", "x": 0.6812917849238023, "y": -0.2648075564231581, "size": 10, "id": "CFI", "group": 1.5, "name": "CFI"}, {"Name": "C5", "d": 1.9059032485708656, "color": "rgb(51, 204, 51)", "type": "square", "label": "C5", "x": 0.20310846566736865, "y": -0.14000473240635625, "size": 10, "id": "C5", "group": 1.5, "name": "C5"}, {"Name": "C3", "d": 1.2907638114922122, "color": "rgb(102, 255, 51)", "type": "square", "label": "C3", "x": 0.41046161409628396, "y": -0.22972594268593455, "size": 10, "id": "C3", "group": 1.5, "name": "C3"}, {"Name": "C4BPA", "d": 2.8208380551350274, "color": "rgb(51, 204, 51)", "type": "square", "label": "C4BPA", "x": 0.32691273713760793, "y": -0.44002575152714773, "size": 10, "id": "C4BPA", "group": 1.5, "name": "C4BPA"}, {"Name": "CPB2", "d": 1.7868069138182534, "color": "rgb(51, 204, 51)", "type": "square", "label": "CPB2", "x": 0.005918725090868364, "y": -0.21370077890922853, "size": 10, "id": "CPB2", "group": 1.5, "name": "CPB2"}, {"Name": "FN1", "d": 0.5457280631776094, "color": "rgb(153, 255, 51)", "type": "square", "label": "FN1", "x": 0.10773970106896563, "y": -0.26910493560594656, "size": 10, "id": "FN1", "group": 0.5, "name": "FN1"}, {"Name": "DCN", "d": 0.07523726540508413, "color": "rgb(204, 255, 51)", "type": "square", "label": "DCN", "x": -0.1449109959561148, "y": -0.5406452007869201, "size": 10, "id": "DCN", "group": 0.5, "name": "DCN"}, {"Name": "KRT13", "d": -5.884559807904592, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT13", "x": -0.5462663294859071, "y": 0.2179240739679362, "size": 10, "id": "KRT13", "group": 2.0, "name": "KRT13"}, {"Name": "KRT14", "d": -5.979503966095012, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT14", "x": 0.20723970121617027, "y": 0.3335529310573068, "size": 10, "id": "KRT14", "group": 2.0, "name": "KRT14"}, {"Name": "KRT5", "d": -7.3912122103291935, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT5", "x": 0.001680558082502895, "y": 0.1554556904769493, "size": 10, "id": "KRT5", "group": 2.0, "name": "KRT5"}, {"Name": "KRT6A", "d": -7.364973261442935, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT6A", "x": -0.3511002998605662, "y": 0.3925879058088128, "size": 10, "id": "KRT6A", "group": 2.0, "name": "KRT6A"}, {"Name": "CSTA", "d": -3.8792306737039226, "color": "rgb(255, 0, 0)", "type": "circle", "label": "CSTA", "x": -0.010849204611034656, "y": 0.7467591168222204, "size": 10, "id": "CSTA", "group": 2.0, "name": "CSTA"}, {"Name": "DSP", "d": -1.7563342206781023, "color": "rgb(255, 153, 51)", "type": "circle", "label": "DSP", "x": -0.0678748213735474, "y": 0.39925246533037617, "size": 10, "id": "DSP", "group": -2.0, "name": "DSP"}, {"Name": "PI3", "d": -4.265528188882317, "color": "rgb(255, 0, 0)", "type": "circle", "label": "PI3", "x": 0.031056762567423294, "y": 1.0, "size": 10, "id": "PI3", "group": 2.0, "name": "PI3"}, {"Name": "KRT16", "d": -4.794121223753202, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT16", "x": 0.03152510498669397, "y": 0.06575226291390532, "size": 10, "id": "KRT16", "group": 2.0, "name": "KRT16"}, {"Name": "KRT15", "d": -4.518290478769958, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT15", "x": -0.2895261348950512, "y": 0.2654866500427342, "size": 10, "id": "KRT15", "group": 2.0, "name": "KRT15"}, {"Name": "KRT6B", "d": -5.5864948168835005, "color": "rgb(255, 0, 0)", "type": "circle", "label": "KRT6B", "x": -0.5348816001833466, "y": 0.32709964482335674, "size": 10, "id": "KRT6B", "group": 2.0, "name": "KRT6B"}], "edges": [{"from": "SFTPD", "to": "DCN", "group": "custom"}, {"from": "FGG", "to": "FGB", "group": "custom"}, {"from": "FGG", "to": "FN1", "group": "custom"}, {"from": "FGB", "to": "FN1", "group": "custom"}, {"from": "CFI", "to": "C3", "group": "custom"}, {"from": "C5", "to": "CPB2", "group": "custom"}, {"from": "C5", "to": "C3", "group": "custom"}, {"from": "C3", "to": "FN1", "group": "custom"}, {"from": "C4BPA", "to": "FN1", "group": "custom"}, {"from": "CPB2", "to": "FN1", "group": "custom"}, {"from": "FN1", "to": "KRT5", "group": "custom"}, {"from": "FN1", "to": "KRT16", "group": "custom"}, {"from": "FN1", "to": "DCN", "group": "custom"}, {"from": "KRT13", "to": "KRT6B", "group": "custom"}, {"from": "KRT13", "to": "KRT6A", "group": "custom"}, {"from": "KRT14", "to": "KRT5", "group": "custom"}, {"from": "KRT5", "to": "KRT15", "group": "custom"}, {"from": "KRT5", "to": "KRT16", "group": "custom"}, {"from": "KRT5", "to": "DSP", "group": "custom"}, {"from": "KRT6A", "to": "KRT15", "group": "custom"}, {"from": "KRT6A", "to": "DSP", "group": "custom"}, {"from": "CSTA", "to": "DSP", "group": "custom"}, {"from": "CSTA", "to": "PI3", "group": "custom"}, {"from": "DSP", "to": "KRT16", "group": "custom"}, {"from": "KRT15", "to": "KRT6B", "group": "custom"}]}'
-                    style="height: 100%; width: 100vw; display: block;"></network-expander>
+                    network='{
+                      "nodes": [{"name": "ENSG00000171862", "id": "ENSG00000171862", "group": "0.5"}, {"name": "ENSG00000284792", "id": "ENSG00000284792", "group": 0.5}],
+                      "edges": []
+                    }'
+                    style="height: 100%; width: 100vw; display: block;"
+                    ></network-expander>
 </div>
+
 <br>
 <br>
 <br>
@@ -88,13 +94,8 @@
     netexp.setAttribute('network', JSON.stringify({
       nodes: [
         {
-          label: "PTEN",
-          id: "PTEN",
-          group: "genes"
-        },
-        {
-          label: "TP53",
-          id: "TP53",
+          name: "ENSG00000171862",
+          id: "ENSG00000171862",
           group: "genes"
         }
       ],
-- 
GitLab