From e5f52d696ed5da37feaa9f67b74e36a004bf1839 Mon Sep 17 00:00:00 2001 From: "Hartung, Michael" <michael.hartung@uni-hamburg.de> Date: Fri, 15 Jul 2022 09:37:18 +0200 Subject: [PATCH] bugfix: select node when expression pie charts does not break chart style anymore --- .../analysis-panel.component.ts | 42 +++++++-------- .../components/network/network.component.ts | 32 ++++++++---- src/app/config.ts | 2 + src/app/interfaces.ts | 1 + src/app/network-settings.ts | 51 ++++--------------- .../explorer-page/explorer-page.component.ts | 3 +- src/app/utils.ts | 5 +- src/index.html | 28 ++-------- 8 files changed, 62 insertions(+), 102 deletions(-) diff --git a/src/app/components/analysis-panel/analysis-panel.component.ts b/src/app/components/analysis-panel/analysis-panel.component.ts index 3e9f514f..a47f0aa7 100644 --- a/src/app/components/analysis-panel/analysis-panel.component.ts +++ b/src/app/components/analysis-panel/analysis-panel.component.ts @@ -10,9 +10,9 @@ import { 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 { HttpClient } from '@angular/common/http'; +import { environment } from '../../../environments/environment'; +import { algorithmNames, AnalysisService } from '../../services/analysis/analysis.service'; import { Drug, EdgeType, @@ -28,9 +28,9 @@ import { 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 { 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'; @@ -60,7 +60,7 @@ interface Baited { }) export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit { - @ViewChild('networkWithLegend', {static: false}) networkWithLegendEl: ElementRef; + @ViewChild('networkWithLegend', { static: false }) networkWithLegendEl: ElementRef; @Input() token: string | null = null; @Input() public set config(config: IConfig | undefined) { @@ -80,7 +80,7 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit public myConfig: IConfig = JSON.parse(JSON.stringify(defaultConfig)); public network: any; - public nodeData: { nodes: any, edges: any } = {nodes: null, edges: null}; + public nodeData: { nodes: any, edges: any } = { nodes: null, edges: null }; private drugNodes: any[] = []; private drugEdges: any[] = []; public showDrugs = false; @@ -180,14 +180,14 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit this.networkHandler.activeNetwork.seedMap = nodeAttributes.isSeed || {}; // Reset - this.nodeData = {nodes: null, edges: null}; + this.nodeData = { nodes: null, edges: null }; this.networkHandler.activeNetwork.networkEl.nativeElement.innerHTML = ''; this.networkHandler.activeNetwork.networkInternal = null; this.showDrugs = false; // Create - const {nodes, edges} = this.createNetwork(this.result); - this.setInputNetwork.emit({nodes: nodes, edges: edges}); + const { nodes, edges } = this.createNetwork(this.result); + this.setInputNetwork.emit({ 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; @@ -197,12 +197,12 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit this.networkHandler.activeNetwork.networkInternal = new vis.Network(container, this.nodeData, options); - this.tableDrugs = nodes.filter( e => e.drugstoneId && e.drugstoneId.startsWith('d')); + this.tableDrugs = nodes.filter(e => e.drugstoneId && e.drugstoneId.startsWith('d')); this.tableDrugs.forEach((r) => { r.rawScore = r.score; }); - this.tableProteins = nodes.filter( e => e.drugstoneId && e.drugstoneId.startsWith('p')); + this.tableProteins = nodes.filter(e => e.drugstoneId && e.drugstoneId.startsWith('p')); this.tableSelectedProteins = []; this.tableProteins.forEach((r) => { r.rawScore = r.score; @@ -273,14 +273,14 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit 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 gradient = (this.networkHandler.activeNetwork.gradientMap !== {}) && (this.networkHandler.activeNetwork.gradientMap[item.id]) ? this.networkHandler.activeNetwork.gradientMap[item.id] : 1.0; const nodeStyled = NetworkSettings.getNodeStyle( node, this.myConfig, isSeed, selected, - gradient - ) + this.networkHandler.activeNetwork.getGradient(item.id), + this.networkHandler.activeNetwork.nodeRenderer + ) updatedNodes.push(nodeStyled); } this.nodeData.nodes.update(updatedNodes); @@ -311,14 +311,14 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit // drugInTrial = node.inTrial; // } const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false; - const gradient = (this.networkHandler.activeNetwork.gradientMap !== {}) && (this.networkHandler.activeNetwork.gradientMap[node.id]) ? this.networkHandler.activeNetwork.gradientMap[node.id] : 1.0; const nodeStyled = NetworkSettings.getNodeStyle( node, this.myConfig, isSeed, selected, - gradient - ) + this.networkHandler.activeNetwork.getGradient(node.id), + this.networkHandler.activeNetwork.nodeRenderer + ) updatedNodes.push(nodeStyled); }); this.nodeData.nodes.update(updatedNodes); @@ -531,7 +531,7 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit public createNetwork(result: any): { edges: any[], nodes: any[] } { const config = result.parameters.config; this.myConfig = config; - + const identifier = this.myConfig.identifier; // add drugGroup and foundNodesGroup for added nodes @@ -572,7 +572,7 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit } // 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)) + nodes.push(NetworkSettings.getNodeStyle(nodeDetails as Node, config, false, false, 1, this.networkHandler.activeNetwork.nodeRenderer)) } // remove self-edges/loops diff --git a/src/app/components/network/network.component.ts b/src/app/components/network/network.component.ts index 529f5fcc..df290410 100644 --- a/src/app/components/network/network.component.ts +++ b/src/app/components/network/network.component.ts @@ -79,6 +79,8 @@ export class NetworkComponent implements OnInit { public fullscreen = false; + public nodeRenderer = null; + constructor(public networkHandler: NetworkHandlerService, public analysis: AnalysisService, public drugstoneConfig: DrugstoneConfigService, public netex: NetexControllerService, public omnipath: OmnipathControllerService) { } ngOnInit(): void { @@ -267,7 +269,12 @@ export class NetworkComponent implements OnInit { public selectTissue(tissue: Tissue | null) { this.expressionExpanded = false; if (!tissue) { + // delete expression values + this.expressionMap = {}; + // delete gradient map + this.gradientMap = {}; this.selectedTissue = null; + this.nodeRenderer = null; const updatedNodes = []; // for (const item of this.proteins) { for (const item of this.currentViewProteins) { @@ -289,14 +296,14 @@ export class NetworkComponent implements OnInit { this.drugstoneConfig.config, false, this.analysis.inSelection(getWrapperFromNode(item)), - 1.0 + 1.0, + this.nodeRenderer ) ) updatedNodes.push(node); } this.nodeData.nodes.update(updatedNodes); - // delete expression values - this.expressionMap = undefined; + } else { this.selectedTissue = tissue const minExp = 0.3; @@ -310,6 +317,7 @@ export class NetworkComponent implements OnInit { this.netex.tissueExpressionGenes(this.selectedTissue, proteinNodes).subscribe((response) => { this.expressionMap = response; const updatedNodes = []; + this.nodeRenderer = pieChartContextRenderer; // mapping from netex IDs to network IDs, TODO check if this step is necessary const networkIdMappping = {} this.nodeData.nodes.forEach(element => { @@ -324,6 +332,7 @@ export class NetworkComponent implements OnInit { } const wrapper = getWrapperFromNode(node) const gradient = expressionlvl !== null ? (Math.pow(expressionlvl / maxExpr, 1 / 3) * (1 - minExp) + minExp) : -1; + this.gradientMap[drugstoneId] = gradient; const pos = this.networkInternal.getPositions([networkId]); node.x = pos[networkId].x; node.y = pos[networkId].y; @@ -333,11 +342,10 @@ export class NetworkComponent implements OnInit { this.drugstoneConfig.config, node.isSeed, this.analysis.inSelection(wrapper), - gradient)); - - // custom ctx renderer for pie chart - node.shape = 'custom'; - node.ctxRenderer = pieChartContextRenderer; + gradient, + this.nodeRenderer + ) + ); updatedNodes.push(node); } this.nodeData.nodes.update(updatedNodes); @@ -368,6 +376,10 @@ export class NetworkComponent implements OnInit { } } + public getGradient(nodeId: string) { + return (this.gradientMap !== {}) && (this.gradientMap[nodeId]) ? this.gradientMap[nodeId] : 1.0; + } + /** * To highlight the seeds in the analysis network, not used in the browser network * @param bool @@ -388,7 +400,6 @@ export class NetworkComponent implements OnInit { node.x = pos[item.id].x; node.y = pos[item.id].y; const isSeed = this.highlightSeeds ? this.seedMap[node.id] : false; - const gradient = (this.gradientMap !== {}) && (this.gradientMap[item.id]) ? this.gradientMap[item.id] : 1.0; Object.assign( node, NetworkSettings.getNodeStyle( @@ -396,7 +407,8 @@ export class NetworkComponent implements OnInit { this.drugstoneConfig.config, isSeed, this.analysis.inSelection(getWrapperFromNode(item)), - gradient + this.getGradient(item.id), + this.nodeRenderer ) ) updatedNodes.push(node); diff --git a/src/app/config.ts b/src/app/config.ts index 8529a7f1..13229016 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -13,6 +13,7 @@ export interface NodeGroup { borderWidthSelected?: number; background?: any; shadow?: any; + ctxRenderer?: any; } export interface EdgeGroup { @@ -132,6 +133,7 @@ export const defaultConfig: IConfig = { // this default group is used for default node group values // and is fallback in case user does not provide any nodeGroup groupName: 'Default Node Group', + ctxRenderer: null, color: { border: '#FFFF00', background: '#FFFF00', diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts index 59daa8bf..8f5eacee 100644 --- a/src/app/interfaces.ts +++ b/src/app/interfaces.ts @@ -1,6 +1,7 @@ import {AlgorithmType, QuickAlgorithmType} from './services/analysis/analysis.service'; export interface Node { + ctxRenderer?: any; label: string; symbol: string; id: string; diff --git a/src/app/network-settings.ts b/src/app/network-settings.ts index b8e1ae85..38881ef2 100644 --- a/src/app/network-settings.ts +++ b/src/app/network-settings.ts @@ -59,15 +59,6 @@ export class NetworkSettings { enabled: false }; - // Node size - private static hostSize = 20; - private static drugSize = 15; - - // Node shape - private static hostShape = 'ellipse'; - private static drugNotInTrialShape = 'box'; - private static drugInTrialShape = 'triangle'; - static getOptions(network: 'main' | 'analysis' | 'analysis-big', physicsOn) { if (network === 'main') { return { @@ -90,43 +81,16 @@ export class NetworkSettings { } } - static getColor(color: 'protein' | 'approvedDrug' | 'unapprovedDrug' | 'hostFont' | 'drugFont' | - 'nonSeedHost' | 'selectedForAnalysis' | 'selectedForAnalysisText' | - 'edgeHostDrug' | 'edgeHostDrugHighlight' | 'edgeGeneGene' | 'edgeGeneGeneHighlight') - /** - * Collection of all colors per use-case - */ { - if (color === 'protein') { - return this.hostColor; - } else if (color === 'approvedDrug') { - return this.approvedDrugColor; - } else if (color === 'unapprovedDrug') { - return this.unapprovedDrugColor; - } else if (color === 'hostFont') { - return this.hostFontColor; - } else if (color === 'drugFont') { - return this.drugFontColor; - } else if (color === 'nonSeedHost') { - return this.nonSeedHostColor; - } else if (color === 'edgeHostDrug') { - return this.edgeHostDrugColor; - } else if (color === 'edgeHostDrugHighlight') { - return this.edgeHostDrugHighlightColor; - } else if (color === 'edgeGeneGene') { - return this.edgeGeneGeneColor; - } else if (color === 'edgeGeneGeneHighlight') { - return this.edgeGeneGeneHighlightColor; - } - } - static getNodeStyle( node: Node, config: IConfig, isSeed: boolean, isSelected: boolean, - gradient: number = 1): Node { + gradient: number = 1, + renderer = null): Node { // delete possible old styles Object.keys(config.nodeGroups.default).forEach(e => delete node[e]); + // set group styles if (node.group === 'default') { node = merge(node, config.nodeGroups.default); @@ -135,7 +99,7 @@ export class NetworkSettings { } // note that seed and selected node style are applied after the node style is fetched. - // this allows to overwrite only attributes of interest, therefor in e.g. seedNode group + // this allows to overwrite only attributes of interest, therefore in e.g. seedNode group // certain attributes like shape can remain undefined // use lodash merge to not lose deep attributes, e.g. "font.size" if (isSeed) { @@ -147,18 +111,21 @@ export class NetworkSettings { // apply selected node style to node node = merge(node, config.nodeGroups.selectedNode); } - // show image if image url is given. If seed nodes are highlighted, ignore image property if (node.image && !isSeed) { node.shape = 'image'; } - // use opactiy as gradient if (gradient === null) { node.opacity = 0 } else { node.opacity = gradient } + // custom ctx renderer for e.g. pie chart + if (renderer !== null) { + node.shape = 'custom'; + node.ctxRenderer = renderer; + } 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 ecd333c4..2a83f988 100644 --- a/src/app/pages/explorer-page/explorer-page.component.ts +++ b/src/app/pages/explorer-page/explorer-page.component.ts @@ -146,7 +146,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.drugstoneConfig.config, false, selected, - 1.0 + this.networkHandler.activeNetwork.getGradient(wrapper.id), + this.networkHandler.activeNetwork.nodeRenderer ) nodeStyled.x = pos[wrapper.id].x; nodeStyled.y = pos[wrapper.id].y; diff --git a/src/app/utils.ts b/src/app/utils.ts index c81884d5..c7116154 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -162,7 +162,6 @@ export function pieChartContextRenderer({ctx, x, y, state: {selected, hover}, st ctx.drawPie = function (style, x, y) { const total = 1; - // draw shadow if (style.shadow) { ctx.save() @@ -183,7 +182,6 @@ export function pieChartContextRenderer({ctx, x, y, state: {selected, hover}, st // prepare pi-chart ctx.fillStyle = style.color ? style.color : 'rgba(255, 0, 0, 1)'; // set alpha value to 1 - // ctx.fillStyle = ctx.fillStyle.replace(/[^,]+(?=\))/, '1') ctx.fillStyle = RGBAtoRGB(ctx.fillStyle) ctx.beginPath(); ctx.moveTo(x, y); @@ -195,7 +193,7 @@ export function pieChartContextRenderer({ctx, x, y, state: {selected, hover}, st // removing shadow application of future fill or stroke calls ctx.restore(); } - ctx.strokeStyle = "black"; + ctx.strokeStyle = style.borderColor ? style.borderColor : 'black'; ctx.lineWidth = selected ? 3 : 2; if (style.opacity !== total) { // avoid the inner line when circle is complete @@ -205,6 +203,7 @@ export function pieChartContextRenderer({ctx, x, y, state: {selected, hover}, st // draw the surrounding border circle ctx.beginPath(); ctx.arc(x, y, style.size, 0, 2 * Math.PI); + ctx.strokeStyle = style.borderColor ? style.borderColor : 'black'; ctx.stroke(); } diff --git a/src/index.html b/src/index.html index 668db0dd..3a24b363 100644 --- a/src/index.html +++ b/src/index.html @@ -49,31 +49,9 @@ <drugst-one id="netexp1" pluginId="2" - config='{"nodeGroups":{ - "proteinCoding":{"type":"gene","color":{background:"#116466",border:"#000000"} ,"font":{"color":"#000000"},"borderWidth":2,"groupName":"gene","shape":"square", "id":"proteinCoding"}, - "up":{"type":"gene","color":{background:"#FC6566",border:"#000000"},"font":{"color":"#000000"},"borderWidth":2,"groupName":"up-regulated gene","shape":"square","id":"up"}, - "down":{"type":"gene","color":{background:"#1158E9",border:"#000000"},"font":{"color":"#000000"},"borderWidth":2,"groupName":"down-regulated gene","shape":"square","id":"down"}, - "unchanged":{"type":"gene","color":{background:"#A0B89D",border:"#000000"},"font":{"color":"#000000"},"borderWidth":2,"groupName":"unchanged gene","shape":"square","id":"unchanged"}, - "metMeta":{"type":"metabolite","color":{background:"#E8A87C",border:"#000000"},"font":{"color":"#000000"},"borderWidth":2,"groupName":"metabolite","shape":"dot","id":"metMeta"}, - "trait":{"type":"trait","color":"#B4CADB","font":{"color":"#000000"},"groupName":"trait","shape":"ellipse","id":"trait"}, - "traitMeta":{"type":"trait","color":"#8c3d37","font":{"color":"#000000"},"groupName":"traitMeta","shape":"star","id":"traitMeta"}, - "foundDrug":{"type":"drug","color":"#B186B4","font":{"color":"#000000"},"groupName":"drug","shape":"diamond"}, - "defaultDisorder":{"type":"disorder","color":"#F0E48C","font":{"color":"#000000"},"groupName":"disorder","shape":"triangle"} - }, - "edgeGroups":{ - "GENETIC_ASSOCIATION":{"type":"GENETIC_ASSOCIATION", "groupName":"mQTL","color":"#A9A9A9","dashes":false,"arrowStrikethrough":false}, - "GENETIC_TRAIT_ASSOCIATION":{"type":"GENETIC_TRAIT_ASSOCIATION","groupName":"mQTL","color":"#b4cadb33","dashes":false,"arrowStrikethrough":false}, - "COABUNDANCE":{"type":"COABUNDANCE","groupName":"coabundance","color":"#5a735a","dashes":false,"arrowStrikethrough":false}, - "METABOLIC_ASSOCIATION":{"type":"METABOLIC_ASSOCIATION","groupName":"traitQTL/mWAS","color":"#b4cadb33","dashes":false,"arrowStrikethrough":false}, - "COREGULATION":{"type":"COREGULATION","groupName":"coregulation","color":"#1db81d","dashes":false,"arrowStrikethrough":false}, - "PARTIAL_CORRELATION":{"type":"PARTIAL_CORRELATION","groupName":"partial correlation","color":"#FFC31E","dashes":false,"arrowStrikethrough":false}, - "COEXPRESSION":{"type":"COEXPRESSION","groupName":"coexpression","color":"#82B81D","dashes":false,"arrowStrikethrough":false}}, - "identifier":"symbol","title":"Drugst.one network","sidebarPos":"right","showOverview":true,"nodeShadow":false,"edgeShadow":false,"showLegend":true, - "showFooter":true, "showSimpleAnalysis":true,"showAdvAnalysis":true,"showSelection":true, - "autofillEdges":false,"physicsOn":true}' - network='{"nodes": [{"type": "gene", "id": "ZNF235", "group": "proteinCoding", "shape": "circle", "label": "ZNF235", "x": 45.1427, "y": 183.494915, "value": 43}, {"type": "gene", "color": {"background": "#116466", "border": "#000000"}, "font": {"color": "#000000"}, "border_width": 2, "shape": "square", "id": "CEACAM16", "border_width_selected": 0, "shadow": false, "group": "proteinCoding", "label": "CEACAM16", "x": -257.39927, "y": 500, "value": 39, "scaling.min": 2, "scaling.max": 54, "physics": false}], - "edges":[{"from":"ZNF235","to":"ZNF235"}] - }'> + config='{"nodeGroups":{"patient":{"type":"patient","color":"#000000","font":{"color":"#000000"},"groupName":"Patient","shape":"image","image":"https://static.thenounproject.com/png/22780-200.png"},"condition":{"type":"condition","color":"#000000","font":{"color":"#000000"},"groupName":"Condition","shape":"text"},"important":{"type":"gene","color":"#ff881f","font":{"color":"#000000"},"groupName":"Important Gene","shape":"star"},"gene":{"type":"gene","color":"#4da300","font":{"color":"#f0f0f0"},"groupName":"Gene","shape":"circle"},"foundDrug":{"type":"drug","color":"#F12590","font":{"color":"#000000"},"groupName":"Drug","shape":"diamond"}},"edgeGroups":{"genotype":{"color":"#000000","groupName":"Relevant Gene"},"has-condition":{"color":"#000000","groupName":"Has Condition","dashes":[2,2]},"default":{"color":"#000000","groupName":"default edge"},"ggi":{"color":"#000000","groupName":"Interaction","dashes":[3,2]}},"identifier":"symbol","title":"Breast cancer example network","nodeShadow":true,"edgeShadow":false,"autofillEdges":false,"showLegend":true}' + network='{"nodes":[{"id":"patient-1","group":"patient","x":592,"y":446},{"id":"patient-2","group":"patient","x":235,"y":87},{"id":"patient-3","group":"patient","x":105,"y":369},{"id":"ATM","label":"ATM","group":"gene","x":289,"y":242},{"id":"BARD1","label":"BARD1","group":"gene","x":44,"y":250},{"id":"BRCA1","label":"BRCA1","group":"gene","x":466,"y":576},{"id":"BRCA2","label":"BRCA2","group":"gene","x":507,"y":285},{"id":"BRIP1","label":"BRIP1","group":"gene","x":54,"y":474},{"id":"CHEK2","label":"CHEK2","group":"gene","x":216,"y":590},{"id":"CDH1","label":"CDH1","group":"gene","x":320,"y":-57},{"id":"NF1","label":"NF1","group":"gene","x":481,"y":111},{"id":"NBN","label":"NBN","group":"gene","x":-57,"y":314},{"id":"PALB2","label":"PALB2","group":"gene","x":450,"y":190},{"id":"PTEN","label":"PTEN","group":"important","x":305,"y":494},{"id":"RAD51C","label":"RAD51C","group":"gene","x":182,"y":-90},{"id":"RAD51D","label":"RAD51D","group":"gene","x":368,"y":73},{"id":"STK11","label":"STK11","group":"gene","x":686,"y":330},{"id":"TP53","label":"TP53","group":"important","x":333,"y":316},{"id":"subtype-1","label":"Subtype 1","group":"condition","x":556,"y":171},{"id":"subtype-2","label":"Subtype 2","group":"condition","x":-87,"y":221}],"edges":[{"from":"BRCA1","to":"BRCA2","group":"ggi"},{"from":"ATM","to":"BARD1","group":"ggi"},{"from":"BRCA1","to":"CHEK2","group":"ggi"},{"from":"RAD51C","to":"RAD51D","group":"ggi"},{"from":"STK11","to":"TP53","group":"ggi"},{"from":"TP53","to":"PALB2","group":"ggi"},{"from":"TP53","to":"RAD51D","group":"ggi"},{"from":"TP53","to":"NF1","group":"ggi"},{"from":"TP53","to":"BRCA1","group":"ggi"},{"from":"TP53","to":"BRCA2","group":"ggi"},{"from":"PTEN","to":"BRCA1","group":"ggi"},{"from":"PTEN","to":"BRCA2","group":"ggi"},{"from":"TP53","to":"PTEN","group":"ggi"},{"from":"ATM","to":"PTEN","group":"ggi"},{"from":"CDH1","to":"RAD51D","group":"ggi"},{"from":"CDH1","to":"PALB2","group":"ggi"},{"from":"NBN","to":"BRIP1","group":"ggi"},{"from":"BRIP1","to":"PTEN","group":"ggi"},{"from":"patient-1","to":"BRCA1","group":"genotype"},{"from":"patient-1","to":"TP53","group":"genotype"},{"from":"patient-1","to":"BRCA2","group":"genotype"},{"from":"patient-1","to":"PTEN","group":"genotype"},{"from":"patient-2","to":"TP53","group":"genotype"},{"from":"patient-2","to":"NF1","group":"genotype"},{"from":"patient-2","to":"BARD1","group":"genotype"},{"from":"patient-3","to":"TP53","group":"genotype"},{"from":"patient-3","to":"PTEN","group":"genotype"},{"from":"patient-3","to":"NBN","group":"genotype"},{"from":"patient-1","to":"subtype-1","group":"has-condition"},{"from":"patient-2","to":"subtype-1","group":"has-condition"},{"from":"patient-3","to":"subtype-2","group":"has-condition"}]}' + > </drugst-one> </div> -- GitLab