From 7de1bccce20a115a78facf34dc45533ddc93be9f Mon Sep 17 00:00:00 2001 From: Julian Matschinske <julian@vyze.ai> Date: Tue, 27 Oct 2020 08:47:39 +0100 Subject: [PATCH] Create proof of concept --- src/app/app.component.ts | 2 +- .../explorer-page.component.html | 32 +-- .../explorer-page/explorer-page.component.ts | 188 +++++++----------- src/index.html | 36 +++- src/styles.scss | 10 - 5 files changed, 115 insertions(+), 153 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c5920e3c..20d9f209 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -14,7 +14,7 @@ export class AppComponent { // Convert `PopupComponent` to a custom element. const NetworkExpander = createCustomElement(ExplorerPageComponent, {injector}); // Register the custom element with the browser. - customElements.define('explorer-element', NetworkExpander); + customElements.define('network-expander', NetworkExpander); } public toggleMobileMenu() { diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html index ee0ce08e..5bc2d0a8 100644 --- a/src/app/pages/explorer-page/explorer-page.component.html +++ b/src/app/pages/explorer-page/explorer-page.component.html @@ -1,7 +1,6 @@ <div class="is-hidden-mobile" [style.color]="textColor"> <app-launch-analysis [(show)]="showAnalysisDialog" - [target]="analysisDialogTarget" - [dataset]="selectedDataset"> + [target]="analysisDialogTarget"> </app-launch-analysis> <app-custom-proteins [(show)]="showCustomProteinsDialog" [visibleNodes]="currentViewNodes"> @@ -19,31 +18,6 @@ <div> <div class="covex sidebar bar-left"> - <div class="card bar-large"> - <header class="card-header"> - <p class="card-header-title"> - <span class="icon"> - <i class="fas fa-database" aria-hidden="true"></i> - </span> Choose Dataset - </p> - <a (click)="collapseData = !collapseData" data-action="collapse" - class="card-header-icon is-hidden-fullscreen" - aria-label="more options"> - <span class="icon"> - <i *ngIf="collapseData" class="fas fa-angle-down" aria-hidden="true"></i> - <i *ngIf="!collapseData" class="fas fa-angle-left" aria-hidden="true"></i> - </span> - </a> - </header> - <div *ngIf="collapseData"> - <div class="card-content"> - <app-dataset-tile [datasetItems]="datasetItems" [selectedDataset]="selectedDataset" - (selectedDatasetChange)="selectedDataset = $event; createNetwork($event.data)"> - </app-dataset-tile> - </div> - </div> - </div> - <div class="card bar-large"> <header class="card-header"> <p class="card-header-title"> @@ -283,7 +257,7 @@ <div class="tile notification is-danger"> <div class="align-vmiddle"> <div class="digit"><i class="fa fa-fast-forward"></i></div> - <button (click)="analysis.startQuickAnalysis(true, selectedDataset)" + <button (click)="analysis.startQuickAnalysis(true, null)" [disabled]="analysis.isLaunchingQuick()" class="button is-white is-rounded has-tooltip" data-tooltip="Find drugs for all proteins."> <span class="icon"> @@ -311,7 +285,7 @@ <div class="tile notification is-info"> <div class="align-vmiddle"> <div class="digit">2</div> - <button (click)="analysis.startQuickAnalysis(false, selectedDataset)" + <button (click)="analysis.startQuickAnalysis(false, null)" [disabled]="analysis.getCount() === 0 || analysis.isLaunchingQuick()" class="button is-white is-rounded has-tooltip" data-tooltip="Find drugs for the selected proteins."> diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts index d37ed9d4..f3e70f17 100644 --- a/src/app/pages/explorer-page/explorer-page.component.ts +++ b/src/app/pages/explorer-page/explorer-page.component.ts @@ -10,7 +10,13 @@ import { ViralProtein, Protein, Wrapper, - getWrapperFromViralProtein, getWrapperFromProtein, getNodeIdsFromPVI, getViralProteinNodeId, getProteinNodeId, Dataset, Tissue + getWrapperFromViralProtein, + getWrapperFromProtein, + getNodeIdsFromPVI, + getViralProteinNodeId, + getProteinNodeId, + Dataset, + Tissue } from '../../interfaces'; import {ProteinNetwork, getDatasetFilename} from '../../main-network'; import {HttpClient, HttpParams} from '@angular/common/http'; @@ -29,6 +35,23 @@ declare var vis: any; }) export class ExplorerPageComponent implements OnInit, AfterViewInit { + private networkJSON = '{"nodes": [], "edges": []}'; + + @Input() + public set network(network: string | undefined) { + if (typeof network === 'undefined') { + return; + } + + this.networkJSON = network; + + this.createNetwork(); + } + + public get network() { + return this.networkJSON; + } + public showDetails = false; public selectedWrapper: Wrapper | null = null; @@ -50,7 +73,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { public effects: any; public edges: any; - private network: any; + private networkInternal: any; public nodeData: { nodes: any, edges: any } = {nodes: null, edges: null}; private dumpPositions = false; @@ -78,46 +101,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { @Input() public textColor = 'red'; - public datasetItems: Dataset[] = [ - { - label: 'SARS-CoV-2 (Gordon et al.)', - strains: 'SARS-CoV-2', - hostTarget: 'Human cell line, HEK-293T kidney cells', - method: 'AP-MS (affinity purification-mass spectrometry)', - source: ['https://www.biorxiv.org/content/10.1101/2020.03.22.002386v3', 'bioRxiv'], - year: 2020, - datasetNames: 'Gordon et al., 2020', - backendId: 'SARS_CoV2', - data: [['Krogan', 'SARS-CoV2']] - }, - { - label: 'SARS-CoV-1 (Pfefferle et al.)', - strains: 'SARS-CoV-1', - hostTarget: 'Human brain and fetal brain cDNA libraries in yeast strains', - method: 'High-Throughput Yeast Two Hybrid Screen (HTY2H) and validations with Lumier assay, ' + - 'as well as experimentally validated interactions from 20 publications.', - source: ['https://www.ncbi.nlm.nih.gov/pubmed/22046132', 'NCBI'], - year: 2011, - datasetNames: 'Pfefferle et al., 2011', - backendId: 'SARS_CoV1', - data: [['Pfefferle', 'SARS-CoV1']] - }, - { - label: 'SARS-CoV-1 (VirHostNet 2.0)', - strains: 'SARS-CoV-1', - hostTarget: 'Different human cell lines', - method: 'Literature curation, interactions from 14 publications, which have experimental validation by at ' + - 'least one of the following assays: co-immunoprecipitation, two hybrid, pull-down, mass spectrometry.', - source: ['http://virhostnet.prabi.fr/', 'VirHostNet 2.0'], - year: 2014, - datasetNames: 'VirHostNet 2.0', - backendId: 'SARS_CoV1', - data: [['VirHostNet', 'SARS-CoV1']] - }, - ]; - - public selectedDataset = this.datasetItems[0]; - @ViewChild('network', {static: false}) networkEl: ElementRef; constructor(private http: HttpClient, public analysis: AnalysisService) { @@ -138,7 +121,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { if (!node) { continue; } - const pos = this.network.getPositions([item.nodeId]); + const pos = this.networkInternal.getPositions([item.nodeId]); node.x = pos[item.nodeId].x; node.y = pos[item.nodeId].y; node.x = pos[item.nodeId].x; @@ -175,20 +158,15 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { } async ngAfterViewInit() { - if (!this.network) { - this.selectedDataset = this.datasetItems[0]; - await this.createNetwork(this.selectedDataset.data); - this.physicsEnabled = false; - } + this.createNetwork(); } - private async getNetwork(dataset: Array<[string, string]>) { - this.currentDataset = 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; + private getNetwork() { + const network = JSON.parse(this.networkJSON); + + this.proteins = network.nodes; + this.effects = []; + this.edges = network.edges; } public reset(event) { @@ -199,7 +177,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { private zoomToNode(id: string) { this.nodeData.nodes.getIds(); - const coords = this.network.getPositions(id)[id]; + const coords = this.networkInternal.getPositions(id)[id]; if (!coords) { return; } @@ -209,7 +187,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { } else { zoomScale = 3.0; } - this.network.moveTo({ + this.networkInternal.moveTo({ position: {x: coords.x, y: coords.y}, scale: zoomScale, animation: true, @@ -229,14 +207,11 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.showDetails = false; } - public async createNetwork(dataset: Array<[string, string]>) { + public async createNetwork() { this.analysis.resetSelection(); this.selectedWrapper = null; - await this.getNetwork(dataset); + this.getNetwork(); this.proteinData = new ProteinNetwork(this.proteins, this.effects, this.edges); - if (!this.dumpPositions) { - await this.proteinData.loadPositions(this.http, dataset); - } this.proteinData.linkNodes(); // Populate baits @@ -262,8 +237,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { const container = this.networkEl.nativeElement; const options = NetworkSettings.getOptions('main'); - this.network = new vis.Network(container, this.nodeData, options); - this.network.on('doubleClick', (properties) => { + this.networkInternal = new vis.Network(container, this.nodeData, options); + this.networkInternal.on('doubleClick', (properties) => { const nodeIds: Array<string> = properties.nodes; if (nodeIds.length > 0) { const nodeId = nodeIds[0]; @@ -277,7 +252,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { } }); - this.network.on('click', (properties) => { + this.networkInternal.on('click', (properties) => { const nodeIds: Array<string> = properties.nodes; if (nodeIds.length > 0) { const nodeId = nodeIds[0]; @@ -288,21 +263,10 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.closeSummary(); } }); - this.network.on('deselectNode', (properties) => { + this.networkInternal.on('deselectNode', (properties) => { this.closeSummary(); }); - - if (this.dumpPositions) { - this.network.on('stabilizationIterationsDone', () => { - // tslint:disable-next-line:no-console - console.log(`${getDatasetFilename(dataset)}`); - // tslint:disable-next-line:no-console - console.log(JSON.stringify(this.network.getPositions())); - }); - this.network.stabilize(); - } - if (this.selectedWrapper) { this.zoomToNode(this.selectedWrapper.nodeId); } @@ -310,7 +274,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.queryItems = []; this.fillQueryItems(this.proteins, this.effects); if (this.selectedWrapper) { - this.network.selectNodes([this.selectedWrapper.nodeId]); + this.networkInternal.selectNodes([this.selectedWrapper.nodeId]); } } @@ -395,7 +359,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { public updatePhysicsEnabled(bool) { this.physicsEnabled = bool; - this.network.setOptions({ + this.networkInternal.setOptions({ physics: { enabled: this.physicsEnabled, stabilization: { @@ -521,7 +485,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { if (!node) { continue; } - const pos = this.network.getPositions([item.nodeId]); + const pos = this.networkInternal.getPositions([item.nodeId]); node.x = pos[item.nodeId].x; node.y = pos[item.nodeId].y; Object.assign(node, @@ -544,38 +508,38 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { const minExp = 0.3; - const params = new HttpParams().set('tissue', `${tissue.id}`).set('data', JSON.stringify(this.currentDataset)); - this.http.get<any>( - `${environment.backend}tissue_expression/`, {params}) - .subscribe((levels) => { - const updatedNodes = []; - const maxExpr = Math.max(...levels.map(lvl => lvl.level)); - for (const lvl of levels) { - const item = getWrapperFromProtein(lvl.protein); - const node = this.nodeData.nodes.get(item.nodeId); - if (!node) { - continue; - } - const gradient = lvl.level !== null ? (Math.pow(lvl.level / maxExpr, 1 / 3) * (1 - minExp) + minExp) : -1; - 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, - this.analysis.inSelection(item), - undefined, - undefined, - gradient)); - node.wrapper = item; - node.gradient = gradient; - this.proteins.find(prot => getProteinNodeId(prot) === item.nodeId).expressionLevel = lvl.level; - (node.wrapper.data as Protein).expressionLevel = lvl.level; - updatedNodes.push(node); - } - this.nodeData.nodes.update(updatedNodes); - }); + // const params = new HttpParams().set('tissue', `${tissue.id}`).set('data', JSON.stringify(this.currentDataset)); + // this.http.get<any>( + // `${environment.backend}tissue_expression/`, {params}) + // .subscribe((levels) => { + // const updatedNodes = []; + // const maxExpr = Math.max(...levels.map(lvl => lvl.level)); + // for (const lvl of levels) { + // const item = getWrapperFromProtein(lvl.protein); + // const node = this.nodeData.nodes.get(item.nodeId); + // if (!node) { + // continue; + // } + // const gradient = lvl.level !== null ? (Math.pow(lvl.level / maxExpr, 1 / 3) * (1 - minExp) + minExp) : -1; + // 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, + // this.analysis.inSelection(item), + // undefined, + // undefined, + // gradient)); + // node.wrapper = item; + // node.gradient = gradient; + // this.proteins.find(prot => getProteinNodeId(prot) === item.nodeId).expressionLevel = lvl.level; + // (node.wrapper.data as Protein).expressionLevel = lvl.level; + // updatedNodes.push(node); + // } + // this.nodeData.nodes.update(updatedNodes); + // }); } diff --git a/src/index.html b/src/index.html index ce6e5f2b..af73ff83 100644 --- a/src/index.html +++ b/src/index.html @@ -14,6 +14,40 @@ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css"> </head> <body> - <app-root></app-root> +<app-root></app-root> +<button onclick="setNetwork('netexp1')">Set Network 1</button> +<button onclick="setNetwork('netexp2')">Set Network 2</button> +<div style="border: 3px solid red"> + <network-expander id="netexp1"></network-expander> +</div> +<div style="border: 3px solid red"> + <network-expander id="netexp2"></network-expander> +</div> +<script> + function setNetwork(nw) { + const netexp = document.getElementById(nw); + + netexp.setAttribute('network', JSON.stringify({ + nodes: [ + { + name: "SIRT5", + proteinAc: "Q9NXA8", + proteinName: "NAD-dependent protein deacylase sirtuin-5" + }, + { + name: "RPL36", + proteinAc: "Q9Y3U8", + proteinName: "60S ribosomal protein L36" + }, + { + name: "G3BP2", + proteinAc: "Q9UN86", + proteinName: "Ras GTPase-activating protein-binding protein 2" + } + ], + edges: [] + })); + } +</script> </body> </html> diff --git a/src/styles.scss b/src/styles.scss index d2b730c5..7dbb0d28 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -16,16 +16,6 @@ $info: $primary; @import "~primeng/resources/primeng.min.css"; @import "~primeicons/primeicons.css"; -html { - overflow: hidden; - height: 100%; -} - -body { - overflow: hidden; - height: 100%; -} - nav.navbar { height: 60px; } -- GitLab