diff --git a/app-test/legend_image.html b/app-test/legend_image.html index dbb6b44a8c6508df549f7552fd9ee0ef4b80bcf1..9a0dda04c901ea7d91b81818053660ab30732e01 100644 --- a/app-test/legend_image.html +++ b/app-test/legend_image.html @@ -41,8 +41,7 @@ "showQuery": false, "legendPos": "right", "nodeGroups": {"default": {"color": "grey", "name": "Default Group", "shape": "triangle"} }, - "edgeGroups":{"default": {"color": "grey", "name": "Default Edge Group"}, "custom": {"color": "red", "name": "Custom Edge Group"}}, - "legendUrl": "https://exbio.wzw.tum.de/covex/assets/leg1.png" + "edgeGroups":{"default": {"color": "grey", "name": "Default Edge Group"}, "custom": {"color": "red", "name": "Custom Edge Group"}} }' style="height: 100vh"></network-expander> </div> diff --git a/src/app/components/analysis-panel/analysis-panel.component.html b/src/app/components/analysis-panel/analysis-panel.component.html index f2b31d21e6aa08b502d585eff39b63832f77f0e9..f282961b9985c2934c022c7538b6a51e23432014 100644 --- a/src/app/components/analysis-panel/analysis-panel.component.html +++ b/src/app/components/analysis-panel/analysis-panel.component.html @@ -29,6 +29,14 @@ <div> <table class="table is-narrow"> <tbody> + <tr *ngIf="result && result.geneInteractionDataset !== undefined"> + <td>Protein-Protein Interaction Dataset</td> + <td>{{result.geneInteractionDataset.name}} (Version {{result.geneInteractionDataset.version}})</td> + </tr> + <tr *ngIf="result && result.drugInteractionDataset !== undefined"> + <td>Protein-Drug Interaction Dataset</td> + <td>{{result.drugInteractionDataset.name}} (Version {{result.drugInteractionDataset.version}})</td> + </tr> <tr *ngIf="task.info.parameters.resultSize !== undefined"> <td>Result Size</td> <td>{{task.info.parameters.resultSize}}</td> @@ -142,8 +150,11 @@ <div class="network-footer-toolbar-inner-container"> <ng-container *ngIf="myConfig.showFooterButtonScreenshot"> - <button class="button is-primary is-rounded has-tooltip network-footer-toolbar-element" - pTooltip="Take a screenshot of the current network." tooltipStyleClass="drgstn" tooltipPosition="top" (click)="toImage()"> + <button class="button is-primary is-rounded has-tooltip network-footer-toolbar-element footer-buttons" + pTooltip="Take a screenshot of the current network." + tooltipStyleClass="drgstn" + tooltipPosition="top" + (click)="toImage()"> <span class="icon"> <i class="fas fa-camera" aria-hidden="true"></i> </span> @@ -153,19 +164,22 @@ </button> </ng-container> - <div class="field network-footer-toolbar-element"> - <p class="control footer-buttons"> - <a [href]="graphmlLink()" class="button is-success is-rounded has-tooltip" - pTooltip="Export this network as .graphml file." tooltipStyleClass="drgstn" tooltipPosition="top"> - <span class="icon"> - <i class="fas fa-download" aria-hidden="true"></i> - </span> - <span [ngClass]="{'text-normal':smallStyle}"> - Export as .graphml - </span> - </a> - </p> - </div> + <ng-container *ngIf="myConfig.showFooterButtonExportGraphml"> + <button + (click)="graphmlLink()" + class="button is-primary is-rounded has-tooltip network-footer-toolbar-element footer-buttons" + pTooltip="Export this network as .graphml file." + tooltipStyleClass="drgstn" + tooltipPosition="top" + > + <span class="icon"> + <i class="fas fa-download" aria-hidden="true"></i> + </span> + <span [ngClass]="{ 'text-normal': smallStyle }" + >.graphml</span + > + </button> + </ng-container> <!-- <div class="field">--> <!-- <p class="control footer-buttons">--> @@ -226,13 +240,13 @@ [value]="highlightSeeds" (valueChange)="updateHighlightSeeds($event)"></app-toggle> <app-toggle *ngIf="task.info.target === 'drug-target'" class="footer-buttons network-footer-toolbar-element" - textOn="Drugs On" textOff="Off" + textOn="Drugs" textOff="Off" tooltipOn="Display adjacent drugs ON." tooltipOff="Display adjacent drugs OFF." [smallStyle]="smallStyle" [value]="adjacentDrugs" (valueChange)="updateAdjacentDrugs($event)"></app-toggle> - <app-toggle class="footer-buttons network-footer-toolbar-element" textOn="Animation On" textOff="Off" + <app-toggle class="footer-buttons network-footer-toolbar-element" textOn="Animation" textOff="Off" tooltipOn="Enable the network animation." tooltipOff="Disable the network animation and freeze nodes." [smallStyle]="smallStyle" diff --git a/src/app/components/analysis-panel/analysis-panel.component.scss b/src/app/components/analysis-panel/analysis-panel.component.scss index 1fa7b34d447650d5eef79ea5c96d79456a224fd1..bf5932e1593df22ffc95fbf12204ce1eed84bf65 100644 --- a/src/app/components/analysis-panel/analysis-panel.component.scss +++ b/src/app/components/analysis-panel/analysis-panel.component.scss @@ -37,7 +37,7 @@ } &.table-tab { // 100% - #{$network-header-height} - #{$analysis-tab-header-height} - normalization-button - height: calc(100% - 9rem); + height: calc(100% - 40px - 1.5rem - 72px); } h4 { diff --git a/src/app/components/analysis-panel/analysis-panel.component.ts b/src/app/components/analysis-panel/analysis-panel.component.ts index cc71e65ee4a17b70e6debe25e72121c975bacb20..e1de31959bd7f3e71a41ea5d3eed28056e008a9f 100644 --- a/src/app/components/analysis-panel/analysis-panel.component.ts +++ b/src/app/components/analysis-panel/analysis-panel.component.ts @@ -32,7 +32,7 @@ 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 { removeDuplicateObjectsFromList } from 'src/app/utils'; +import { downLoadFile, removeDuplicateObjectsFromList } from 'src/app/utils'; declare var vis: any; @@ -70,15 +70,16 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { for (const key of Object.keys(config)) { this.myConfig[key] = config[key]; } + console.log(this.myConfig) } @Output() tokenChange = new EventEmitter<string | null>(); @Output() showDetailsChange = new EventEmitter<Wrapper>(); @Output() visibleItems = new EventEmitter<[any[], [Node[], Tissue], NodeInteraction[]]>(); public task: Task | null = null; + public result: any = null; public myConfig: IConfig = JSON.parse(JSON.stringify(defaultConfig)); - public network: any; private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null}; private drugNodes: any[] = []; @@ -164,8 +165,9 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { } if (this.task && this.task.info.done) { - const result = await this.netex.getTaskResult(this.token); - const nodeAttributes = result.nodeAttributes || {}; + this.result = await this.netex.getTaskResult(this.token); + console.log(this.result) + const nodeAttributes = this.result.nodeAttributes || {}; this.seedMap = nodeAttributes.isSeed || {}; @@ -176,7 +178,7 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { this.showDrugs = false; // Create - const {nodes, edges} = this.createNetwork(result); + const {nodes, edges} = this.createNetwork(this.result); this.nodeData.nodes = new vis.DataSet(nodes); this.nodeData.edges = new vis.DataSet(edges); @@ -394,8 +396,11 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { return `${environment.backend}task_result/?token=${this.token}&view=${view}&fmt=csv`; } - public graphmlLink(): string { - return `${environment.backend}graph_export/?token=${this.token}`; + public graphmlLink() { + const data = {nodes: this.nodeData.nodes.get(), edges: this.nodeData.edges.get()} + this.netex.graphmlLink(data).subscribe(response => { + return downLoadFile(response, "application/xml"); + }) } public inferEdgeGroup(edge: object): EdgeType { @@ -490,8 +495,6 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { nodeDetails.group = nodeDetails.group ? nodeDetails.group : 'default'; nodeDetails.label = nodeDetails.label ? nodeDetails.label : nodeDetails[identifier] } - console.log(nodeDetails) - // IMPORTANT we set seeds to "selected" and not to seeds. The user should be inspired to run // 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, config, false, false, 1)) @@ -621,8 +624,6 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { this.tableSelectedProteins = e; const addItems = []; const removeItems = []; - console.log(e) - for (const i of this.tableSelectedProteins) { const wrapper = getWrapperFromNode(i); if (oldSelection.indexOf(i) === -1) { diff --git a/src/app/components/network-legend/network-legend.component.scss b/src/app/components/network-legend/network-legend.component.scss index e0b54d8c21a9a3342d3a41f2b9c502faf09baebb..0d1cb302632e186cc3b1069216a031b07f32e200 100644 --- a/src/app/components/network-legend/network-legend.component.scss +++ b/src/app/components/network-legend/network-legend.component.scss @@ -4,14 +4,14 @@ div.legend { position: absolute; bottom: 0; - left: 1rem; - bottom: 1rem; + margin-left: 1rem; z-index: $explorer-networklegend-z; &.right { right: 0; } img { max-width: 20vw; + // margin-bottom: 1rem; } td.group-name{ color:var(--drgstn-text-primary); diff --git a/src/app/config.ts b/src/app/config.ts index 62a8ab8c9060c2ad3b3726a9671ce85b363c39e1..ead040c5b92c3de00de30da88495a76caf879562 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -48,6 +48,7 @@ export interface IConfig { showFooter: boolean; showFooterButtonExpression: boolean; showFooterButtonScreenshot: boolean; + showFooterButtonExportGraphml: boolean; showLegend: boolean; showLegendNodes: boolean; showLegendEdges: boolean; @@ -86,6 +87,7 @@ export const defaultConfig: IConfig = { showLegend: true, showFooterButtonExpression: true, showFooterButtonScreenshot: true, + showFooterButtonExportGraphml: true, identifier: 'symbol', interactionDrugProtein: 'DrugBank', interactionProteinProtein: 'STRING', diff --git a/src/app/dialogs/launch-analysis/launch-analysis.component.ts b/src/app/dialogs/launch-analysis/launch-analysis.component.ts index b2e3a5336b28bf4fa523d1cc98252c387bcd3877..526c084f0e3c395c1bc328692f8a80fd14a97fc2 100644 --- a/src/app/dialogs/launch-analysis/launch-analysis.component.ts +++ b/src/app/dialogs/launch-analysis/launch-analysis.component.ts @@ -112,12 +112,9 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { config: this.config, input_network: this.inputNetwork }; - console.log("config") - console.log(this.config) parameters.ppi_dataset = this.config.interactionProteinProtein; parameters.pdi_dataset = this.config.interactionDrugProtein; - console.log(this.target) parameters.target = this.target === 'drug' ? 'drug' : 'drug-target'; // pass network data to reconstruct network in analysis result to connect non-proteins to results // drop interactions in nodes beforehand to no cause cyclic error, information is contained in edges @@ -176,9 +173,6 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { } parameters.hub_penalty = this.multisteinerHubPenalty; } - console.log('parameters') - - console.log(parameters) await this.analysis.startAnalysis(this.algorithm, this.target, parameters); } diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html index e671ec4ee704a7cba82cff9ad0d8445a7f241ff5..f4ead32b1fe07efe2000c2693e5fe7a1a762482d 100644 --- a/src/app/pages/explorer-page/explorer-page.component.html +++ b/src/app/pages/explorer-page/explorer-page.component.html @@ -174,7 +174,7 @@ <button (click)="toImage()" class=" - button + button footer-buttons is-primary is-rounded has-tooltip network-footer-toolbar-element @@ -192,6 +192,28 @@ </button> </ng-container> + <ng-container *ngIf="myConfig.showFooterButtonExportGraphml"> + <button + (click)="graphmlLink()" + class=" + button footer-buttons + is-primary is-rounded + has-tooltip + network-footer-toolbar-element + " + pTooltip="Export this network as .graphml file." + tooltipStyleClass="drgstn" + tooltipPosition="top" + > + <span class="icon"> + <i class="fas fa-download" aria-hidden="true"></i> + </span> + <span [ngClass]="{ 'text-normal': smallStyle }" + >.graphml</span + > + </button> + </ng-container> + <ng-container *ngIf="myConfig.showFooterButtonExpression"> <div class=" @@ -270,7 +292,7 @@ <app-toggle class="footer-buttons network-footer-toolbar-element" - textOn="Animation On" + textOn="Animation" textOff="Off" tooltipOn="Enable the network animation." tooltipOff="Disable the network animation and freeze nodes." diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts index 7a39c3eb996f72b46663043df9cdce5548b1ba0f..118b1b3a28595363c6c603d82dd2be985b10bee3 100644 --- a/src/app/pages/explorer-page/explorer-page.component.ts +++ b/src/app/pages/explorer-page/explorer-page.component.ts @@ -25,7 +25,7 @@ import domtoimage from 'dom-to-image'; import {NetworkSettings} from '../../network-settings'; import {defaultConfig, EdgeGroup, IConfig, InteractionDatabase, NodeGroup} from '../../config'; import {NetexControllerService} from 'src/app/services/netex-controller/netex-controller.service'; -import {removeDuplicateObjectsFromList} from '../../utils' +import {downLoadFile, removeDuplicateObjectsFromList} from '../../utils' import * as merge from 'lodash/fp/merge'; import { AnalysisPanelComponent } from 'src/app/components/analysis-panel/analysis-panel.component'; @@ -336,6 +336,13 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { }); } + public graphmlLink() { + const data = {nodes: this.nodeData.nodes.get(), edges: this.nodeData.edges.get()} + this.netex.graphmlLink(data).subscribe(response => { + return downLoadFile(response, "application/xml"); + }) + } + public async openSummary(item: Wrapper, zoom: boolean) { this.selectedWrapper = item; if (zoom) { diff --git a/src/app/services/analysis/analysis.service.ts b/src/app/services/analysis/analysis.service.ts index 305a40498ea84055f72eb133456b2a4b726289b9..541905eacdd6b0fcc773e5e74f6a870a46f4e8b4 100644 --- a/src/app/services/analysis/analysis.service.ts +++ b/src/app/services/analysis/analysis.service.ts @@ -49,6 +49,8 @@ export class AnalysisService { private selections = new Map<string, Map<string, Wrapper>>(); public tokens: string[] = []; + private tokensCookieKey = `netex-tokens-${window.location.host}`; + private tokensFinishedCookieKey = `netex-finishedTokens-${window.location.host}`; public finishedTokens: string[] = []; public tasks: Task[] = []; @@ -60,8 +62,8 @@ export class AnalysisService { private tissues: Tissue[] = []; constructor(private http: HttpClient, public netex: NetexControllerService) { - const tokens = localStorage.getItem('tokens'); - const finishedTokens = localStorage.getItem('finishedTokens'); + const tokens = localStorage.getItem(this.tokensCookieKey); + const finishedTokens = localStorage.getItem(this.tokensFinishedCookieKey); if (tokens) { this.tokens = JSON.parse(tokens); @@ -80,14 +82,14 @@ export class AnalysisService { this.tokens = this.tokens.filter((item) => item !== token); this.finishedTokens = this.finishedTokens.filter((item) => item !== token); this.tasks = this.tasks.filter((item) => item.token !== (token)); - localStorage.setItem('tokens', JSON.stringify(this.tokens)); + localStorage.setItem(this.tokensCookieKey, JSON.stringify(this.tokens)); } removeAllTasks() { this.tasks = []; this.finishedTokens = []; this.tokens = []; - localStorage.removeItem('tokens'); + localStorage.removeItem(this.tokensCookieKey); } async getTasks() { @@ -261,7 +263,7 @@ export class AnalysisService { }, }).toPromise(); this.tokens.push(resp.token); - localStorage.setItem('tokens', JSON.stringify(this.tokens)); + localStorage.setItem(this.tokensCookieKey, JSON.stringify(this.tokens)); this.startWatching(); toast({ @@ -296,7 +298,7 @@ export class AnalysisService { }).toPromise(); this.tokens.push(resp.token); - localStorage.setItem('tokens', JSON.stringify(this.tokens)); + localStorage.setItem(`netex-tokens-${window.location.host}`, JSON.stringify(this.tokens)); this.startWatching(); } @@ -346,11 +348,11 @@ export class AnalysisService { if (task.info.done) { this.finishedTokens.push(task.token); this.showToast(task, 'DONE'); - localStorage.setItem('finishedTokens', JSON.stringify(this.finishedTokens)); + localStorage.setItem(this.tokensFinishedCookieKey, JSON.stringify(this.finishedTokens)); } else if (task.info.failed) { this.finishedTokens.push(task.token); this.showToast(task, 'FAILED'); - localStorage.setItem('finishedTokens', JSON.stringify(this.finishedTokens)); + localStorage.setItem(this.tokensFinishedCookieKey, JSON.stringify(this.finishedTokens)); } else { } } diff --git a/src/app/services/netex-controller/netex-controller.service.ts b/src/app/services/netex-controller/netex-controller.service.ts index 49772e96ca5a631a3c819fa906ccb3600b72b6d3..4992123d0b3552774823b673c8fe0a54d8e5b1f7 100644 --- a/src/app/services/netex-controller/netex-controller.service.ts +++ b/src/app/services/netex-controller/netex-controller.service.ts @@ -3,7 +3,7 @@ import {environment} from '../../../environments/environment'; import {HttpClient, HttpParams} from '@angular/common/http'; import {AlgorithmType, QuickAlgorithmType} from '../analysis/analysis.service'; import { Observable } from 'rxjs'; -import { Tissue, Node} from 'src/app/interfaces'; +import { Tissue, Node, EdgeType} from 'src/app/interfaces'; import { InteractionDrugProteinDB } from 'src/app/config'; @Injectable({ @@ -107,4 +107,12 @@ export class NetexControllerService { } return this.http.post<any>(`${environment.backend}adjacent_drugs/`, params); } + + public graphmlLink(graph_data: {edges: EdgeType[], nodes: Node[]}) { + /** + * Sends complete graph data to backend where it is written to graphml File. + * The file is returned as download for the user. + */ + return this.http.post(`${environment.backend}graph_export/`, graph_data, {responseType: 'text'}); + } } diff --git a/src/app/utils.ts b/src/app/utils.ts index 72b2bff4a6e7bc80967eb1764c41d88899719be6..e29a04f554154d4ba0fff16cb6f103edaecb4e19 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -119,3 +119,17 @@ export function removeDuplicateObjectsFromList(nodes: Node[], attribute: string) return filteredArray; } +/** + * Method is use to download file. + * @param data - Array Buffer data + * @param type - type of the document. + */ +export function downLoadFile(data: any, type: string) { + let blob = new Blob([data], { type: type}); + var a = document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = 'test.graphml'; + // start download + a.click(); +} + diff --git a/src/index.html b/src/index.html index 22f9f43fc2572549370ac507e59ca80aa4d78898..34f3cec1b089db9f888f56b548adc5b147f9eca6 100644 --- a/src/index.html +++ b/src/index.html @@ -21,6 +21,10 @@ <input type="checkbox" onclick=changeConfigStr('{"showSelection":'+this.checked+'}') checked /> Show Selection<br> <input type="checkbox" onclick=changeConfigStr('{"showFooter":'+this.checked+'}') checked /> Show Footer<br> <input type="checkbox" onclick=changeConfigStr('{"showLegend":'+this.checked+'}') checked /> Show Legend<br> +<input type="checkbox" onclick=changeConfigStr('{"showFooterButtonScreenshot":'+this.checked+'}') checked /> Show Screenshot button<br> +<input type="checkbox" onclick=changeConfigStr('{"showFooterButtonExportGraphml":'+this.checked+'}') checked /> Show Export As Graphml Button<br> + + <input id="new_color" type="text" /> <button onclick=changeColor(document.getElementById("new_color").value) >Set Color </button> <br> <button onclick=changeConfigStr('{"legendPos":"left"}') > Legend to Left </button> <br> <button onclick=changeConfigStr('{"legendPos":"right"}') > Legend to Right </button> <br> @@ -40,7 +44,9 @@ "edgeGroups": {"xxx": {"color": "black", "groupName": "xxx Group", "dashes": [1, 2]}, "notdashes": {"color": "black", "groupName": "not dashes Group"}}, "identifier": "symbol", "nodeShadow": true, - "edgeShadow": true + "edgeShadow": true, + "interactionDrugProtein": "ChEMBL", + "legendUrl": "https://exbio.wzw.tum.de/covex/assets/leg1.png" }' network='{ "nodes": [{"id": "TP53", "group": "0.5"}, {"id": "MYC", "group": "pugGroup"}, {"id": "Patient No. 5", "group": "patientgroup"}, {"label": "PTEN", "id": "PTEN", "group": 0.5, "value":"5"}], diff --git a/src/stylesheets/styles.scss b/src/stylesheets/styles.scss index 851c46738dfb2aa8d54f7a3baf0bf964b81c20e5..be9b0045fa46aec5ef903e70a6b6f77bbaf1aed9 100644 --- a/src/stylesheets/styles.scss +++ b/src/stylesheets/styles.scss @@ -224,7 +224,7 @@ } .footer-buttons { - margin-left: 20px; + margin-left: 10px; margin-right: 10px; }