diff --git a/src/app/config.ts b/src/app/config.ts index 4d792077d0b6660b8516993c7f061fe0bf25817a..31da31f923b7f9303dcd825526b1d85a6b04ab78 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -32,7 +32,7 @@ export type InteractionDrugProteinDB = 'NeDRex' | 'DrugBank' | 'DrugCentral' | ' export type InteractionProteinProteinDB = 'NeDRex' | 'BioGRID' | 'IID' | 'IntAct' | 'STRING' | 'APID'; export type IndicationDrugDisorderDB = 'NeDRex' | 'CTD' | 'DrugCentral' | 'DrugBank'; export type AssociatedProteinDisorderDB = 'NeDRex' | 'DisGeNET' | 'OMIM'; -export type AdvAnalysisContentTypes = 'drug-target-search' | 'drug-search' | 'enrichment-gprofiler'; +export type AdvAnalysisContentTypes = 'drug-target-search' | 'drug-search' | 'enrichment-gprofiler' | 'enrichment-digest'; // TODO: should this be external or integrated in the backend? @@ -145,7 +145,7 @@ export const defaultConfig: IConfig = { showItemSelector: true, showSimpleAnalysis: true, showAdvAnalysis: true, - showAdvAnalysisContent: ['drug-search', 'drug-target-search', 'enrichment-gprofiler'], + showAdvAnalysisContent: ['drug-search', 'drug-target-search', 'enrichment-gprofiler', 'enrichment-digest'], showSelection: true, showTasks: true, showNetworkMenu: 'right', diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html index 28f4e53a3f70ae5e56c55dd190ef40f23cb0a7f8..e76a67a66ee48bd285c8ce9ebad3bcc14eae6873 100644 --- a/src/app/pages/explorer-page/explorer-page.component.html +++ b/src/app/pages/explorer-page/explorer-page.component.html @@ -281,7 +281,7 @@ <div class="control"> <a *ngIf="analysis.getCount() > 0" - [href]="gProfilerLink()" + (click)="openExternal(gProfilerLink())" target="_blank" [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" class="button is-primary is-fullwidth is-rounded has-tooltip" @@ -298,7 +298,7 @@ <span [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" > - Enrichment Analysis + g:Profiler Enrichment Analysis </span> </a> <a @@ -319,7 +319,54 @@ <span [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" > - Enrichment Analysis + g:Profiler Enrichment Analysis + </span> + </a> + </div> + </div> + <div class="field" *ngIf="!drugstoneConfig.config.showAdvAnalysisContent || drugstoneConfig.config.showAdvAnalysisContent.includes('enrichment-digest')"> + <div class="control"> + <a + *ngIf="analysis.getCount() > 0" + (click)="openDigest()" + target="_blank" + [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" + class="button is-primary is-fullwidth is-rounded has-tooltip" + pTooltip="Use enrichment analysis via DIGEST (external)." + [tooltipStyleClass]=" + 'drgstn drgstn-tooltip drgstn-tooltip-top' + " + tooltipPosition="top" + > + <app-fa-solid-icon + classString="first-item-in-button" + icon="external-link-alt" + ></app-fa-solid-icon> + <span + [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" + > + DIGEST Enrichment Analysis + </span> + </a> + <a + *ngIf="analysis.getCount() === 0" + disabled + [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" + class="button is-primary is-fullwidth is-rounded has-tooltip" + pTooltip="Use enrichment analysis via DIGEST (external)." + [tooltipStyleClass]=" + 'drgstn drgstn-tooltip drgstn-tooltip-top' + " + tooltipPosition="top" + > + <app-fa-solid-icon + icon="external-link-alt" + classString="first-item-in-button" + ></app-fa-solid-icon> + <span + [ngClass]="{ 'text-small': drugstoneConfig.smallStyle }" + > + DIGEST Enrichment Analysis </span> </a> </div> diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts index 2292ba0017fa364aec1794b31dc612f1d73f9990..b2ca36514a2e6d180894a12bac0581fd78786c0a 100644 --- a/src/app/pages/explorer-page/explorer-page.component.ts +++ b/src/app/pages/explorer-page/explorer-page.component.ts @@ -78,7 +78,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { try { this.networkJSON = JSON.stringify(typeof network === 'string' ? JSON5.parse(network) : network); } catch { - console.log('ERROR: Failed parsing input network') + console.log('ERROR: Failed parsing input network'); } this.activateConfig(); } @@ -120,8 +120,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { public selectedAnalysisToken: string | null = null; @Input() set taskId(token: string | null) { - if (token == null || token.length === 0) - this.selectedAnalysisToken = null + if (token == null || token.length === 0) { + this.selectedAnalysisToken = null; + } this.selectedAnalysisToken = token; } @@ -158,7 +159,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { selected, this.networkHandler.activeNetwork.getGradient(wrapper.id), this.networkHandler.activeNetwork.nodeRenderer - ) + ); nodeStyled.x = pos[wrapper.id].x; nodeStyled.y = pos[wrapper.id].y; updatedNodes.push(nodeStyled); @@ -257,36 +258,41 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { let node_map = {}; nodes.filter(n => n.drugstoneType === 'protein').forEach(node => { if (typeof node.drugstoneId === 'string') { - if (node_map[node.drugstoneId]) + if (node_map[node.drugstoneId]) { node_map[node.drugstoneId].push(node.id); - else + } else { node_map[node.drugstoneId] = [node.id]; + } } else { node.drugstoneId.forEach(n => { - if (node_map[n]) + if (node_map[n]) { node_map[n].push(node.id); - else + } else { node_map[n] = [node.id]; - }) + } + }); } - }) + }); const netexEdges = await this.netex.fetchEdges(nodes, this.drugstoneConfig.config.interactionProteinProtein, this.drugstoneConfig.config.licensedDatasets); edges.push(...netexEdges.map(netexEdge => mapNetexEdge(netexEdge, this.drugstoneConfig.config, node_map)).flatMap(e => e)); } - const edge_map = {} + const edge_map = {}; edges = edges.filter(edge => { - if (edge_map[edge.to] && edge_map[edge.to].indexOf(edge.from) !== -1) - return false - if (edge_map[edge.from] && edge_map[edge.from].indexOf(edge.to) !== -1) - return false - if (!edge_map[edge.from]) - edge_map[edge.from] = [edge.to] - else - edge_map[edge.from].push(edge.to) - return true - }) + if (edge_map[edge.to] && edge_map[edge.to].indexOf(edge.from) !== -1) { + return false; + } + if (edge_map[edge.from] && edge_map[edge.from].indexOf(edge.to) !== -1) { + return false; + } + if (!edge_map[edge.from]) { + edge_map[edge.from] = [edge.to]; + } else { + edge_map[edge.from].push(edge.to); + } + return true; + }); // @ts-ignore if (!this.drugstoneConfig.selfReferences) { @@ -375,10 +381,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { }); if (network.edges != null) // @ts-ignore + { network.edges.forEach(edge => { edge.from = this.removeEnsemblVersion(edge.from); edge.to = this.removeEnsemblVersion(edge.to); }); + } } // map data to nodes in backend @@ -401,12 +409,13 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { // adjust edge labels accordingly and filter const edges = new Array(); - if (network.edges != null) + if (network.edges != null) { network.edges.forEach(edge => { if (edge.from !== undefined && edge.to !== undefined) { edges.push(edge); } }); + } // remove edges without endpoints network.edges = edges; this.networkHandler.activeNetwork.inputNetwork = network; @@ -423,7 +432,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.networkHandler.activeNetwork.selectedWrapper = item; // add expression information if loaded if (this.networkHandler.activeNetwork.expressionMap && this.networkHandler.activeNetwork.selectedWrapper.id in this.networkHandler.activeNetwork.expressionMap) { - this.networkHandler.activeNetwork.selectedWrapper.expression = this.networkHandler.activeNetwork.expressionMap[this.networkHandler.activeNetwork.selectedWrapper.id] + this.networkHandler.activeNetwork.selectedWrapper.expression = this.networkHandler.activeNetwork.expressionMap[this.networkHandler.activeNetwork.selectedWrapper.id]; } if (zoom) { this.zoomToNode(item.id); @@ -490,7 +499,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { border: group.color, background: group.color } - } + }; } // if image is given, set node shape to image if (group.image) { @@ -539,9 +548,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { // nodes in selection have drugstoneId const queryString = this.analysis.getSelection() .filter(wrapper => wrapper.data.drugstoneType === 'protein') - .map(wrapper => wrapper.data.uniprotAc) + .map(wrapper => wrapper.data.id) .join('%0A'); - return 'http://biit.cs.ut.ee/gprofiler/gost?' + + return 'https://biit.cs.ut.ee/gprofiler/gost?' + 'organism=hsapiens&' + `query=${queryString}&` + 'ordered=false&' + @@ -557,19 +566,44 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { 'background='; } + openExternal(url) { + window.open(url, '_blank').focus(); + } + + async openDigest() { + let proteins = this.analysis.getSelection() + .filter(wrapper => wrapper.data.drugstoneType === 'protein') + .map(wrapper => wrapper.data.id); + let params = { + runs: 1000, + replace: 100, + distance: 'jaccard', + background_model: 'complete', + type: 'gene', + target: proteins, + target_id: this.drugstoneConfig.config.identifier + }; + + let resp = await this.netex.digest_request(params).catch(err => console.error(err)); + let url = 'https://digest-validation.net/result?id=' + resp.task; + this.openExternal(url) + } + //TODO change to access through network service @ViewChild('analysisPanel') analysisPanel; getNodes(): any { - if (this.selectedAnalysisToken && this.analysisPanel) - return this.analysisPanel.getResultNodes() + if (this.selectedAnalysisToken && this.analysisPanel) { + return this.analysisPanel.getResultNodes(); + } return this.networkHandler.activeNetwork.inputNetwork.nodes; } getEdges(): any { - if (this.selectedAnalysisToken && this.analysisPanel) - return this.analysisPanel.getResultEdges() - return this.networkHandler.activeNetwork.inputNetwork.edges + if (this.selectedAnalysisToken && this.analysisPanel) { + return this.analysisPanel.getResultEdges(); + } + return this.networkHandler.activeNetwork.inputNetwork.edges; } diff --git a/src/app/services/netex-controller/netex-controller.service.ts b/src/app/services/netex-controller/netex-controller.service.ts index de49c7b14fae14251b32c5dc46c08723faa08936..f4aa4e376735538b5d78c333cd589fe3c79bc7f5 100644 --- a/src/app/services/netex-controller/netex-controller.service.ts +++ b/src/app/services/netex-controller/netex-controller.service.ts @@ -43,6 +43,10 @@ export class NetexControllerService { return this.http.get<Tissue[]>(`${environment.backend}tissues/`); } + public digest_request(payload): Promise<any> { + return this.http.post('https://api.digest-validation.net/set', payload).toPromise(); + } + public tissueExpressionGenes(tissue: Tissue, nodes: Node[]): Observable<any> { /** * Returns the expression in the given tissue for given nodes and cancerNodes @@ -58,9 +62,9 @@ export class NetexControllerService { public adjacentDisorders(nodes: Node[], nodeType: string, dataset: string, licenced: boolean): Observable<any> { const params = {dataset: dataset, licenced: licenced}; if (nodeType === 'proteins') { - params["proteins"] = nodes.filter((node: Node) => node.drugstoneId && node.drugstoneType === 'protein').flatMap((node: Node) => node.drugstoneId).map(id => id.slice(1)); + params['proteins'] = nodes.filter((node: Node) => node.drugstoneId && node.drugstoneType === 'protein').flatMap((node: Node) => node.drugstoneId).map(id => id.slice(1)); } else if (nodeType === 'drugs') { - params["drugs"] = nodes.map((node: Node) => node.drugId && node.drugstoneType === 'drug' ? node.drugstoneId.slice(2) : undefined).filter(id => id != null); + params['drugs'] = nodes.map((node: Node) => node.drugId && node.drugstoneType === 'drug' ? node.drugstoneId.slice(2) : undefined).filter(id => id != null); } return this.http.post<any>(`${environment.backend}adjacent_disorders/`, params); } diff --git a/src/index.html b/src/index.html index 38fc30b17b1964573c89e0e38ad6e39145b16cf2..010017586642ed624a07f95c7ee1d39110b63f2f 100644 --- a/src/index.html +++ b/src/index.html @@ -5,7 +5,7 @@ <title>Network Expander</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> + <script type="text/javascript" src="http://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> <!-- <link rel="stylesheet" type="text/css" href="./stylesheets/default-theme.css">--> </head>