Skip to content

Commit

Permalink
AG-1660: fix similar genes network chart and table
Browse files Browse the repository at this point in the history
  • Loading branch information
hallieswan authored and EC2 Default User committed Feb 25, 2025
1 parent 519c811 commit afd7caa
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 270 deletions.
30 changes: 16 additions & 14 deletions apps/agora/api/src/components/genes.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
// -------------------------------------------------------------------------- //
// External
// -------------------------------------------------------------------------- //
import { Request, Response, NextFunction } from 'express';
import { NextFunction, Request, Response } from 'express';

// -------------------------------------------------------------------------- //
// Internal
// -------------------------------------------------------------------------- //
import { setHeaders, cache, altCache } from '../helpers';
import { Gene, GeneCollection } from '../models';
import {
getRnaDifferentialExpression,
getProteomicsLFQ,
getProteomicsSRM,
getProteomicsTMT,
getMetabolomics,
getBioDomains,
getExperimentalValidation,
getGeneLinks,
getMetabolomics,
getNeuropathologicCorrelations,
getOverallScores,
getGeneLinks,
getBioDomains,
getProteomicsLFQ,
getProteomicsSRM,
getProteomicsTMT,
getRnaDifferentialExpression,
} from '.';
import { altCache, cache, setHeaders } from '../helpers';
import { Gene, GeneCollection } from '../models';
import { getSimilarGenesNetwork } from './similar_genes_network';

// -------------------------------------------------------------------------- //
// Functions
Expand All @@ -38,12 +39,12 @@ export async function getAllGenes() {
return result;
}

export async function getGenes(ids?: string | string[]) {
export async function getGenes(ids?: string) {
const genes: Gene[] = await getAllGenes();

if (ids) {
ids = typeof ids == 'string' ? ids.split(',') : ids;
return genes.filter((g: Gene) => ids?.includes(g.ensembl_gene_id));
const ids_array = ids.split(',');
return genes.filter((g: Gene) => ids_array.includes(g.ensembl_gene_id));
}

return genes;
Expand Down Expand Up @@ -80,6 +81,7 @@ export async function getGene(ensg: string) {
result.experimental_validation = await getExperimentalValidation(ensg);
result.links = await getGeneLinks(ensg);
result.bio_domains = await getBioDomains(ensg);
result.similar_genes_network = getSimilarGenesNetwork(result);
}

cache.set(cacheKey, result);
Expand Down Expand Up @@ -162,7 +164,7 @@ export async function genesRoute(req: Request, res: Response, next: NextFunction
}

try {
const result = await getGenes(<string | string[]>req.query.ids);
const result = await getGenes(<string>req.query.ids);
setHeaders(res);
res.json(result);
} catch (err) {
Expand Down
95 changes: 95 additions & 0 deletions apps/agora/api/src/components/similar_genes_network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
Gene,
SimilarGenesNetwork,
SimilarGenesNetworkLink,
SimilarGenesNetworkNode,
} from '@sagebionetworks/agora/api-client-angular';

export function getSimilarGenesNetwork(gene: Gene): SimilarGenesNetwork {
const nodes: { [key: string]: SimilarGenesNetworkNode } = {};
const links: { [key: string]: SimilarGenesNetworkLink } = {};
const response: SimilarGenesNetwork = {
nodes: [],
links: [],
min: 0,
max: 0,
};

if (gene.links) {
Object.values(gene.links).forEach((link) => {
const a: string = link.geneA_ensembl_gene_id;
const b: string = link.geneB_ensembl_gene_id;
const key = a + b;
const rKey = b + a;

// Check if a reverse link already exists
if (links[rKey] && !links[rKey].brain_regions.includes(link['brainRegion'])) {
links[rKey].brain_regions.push(link['brainRegion']);
return;
}

if (!links[key]) {
links[key] = {
source: a,
target: b,
source_hgnc_symbol: link?.geneA_external_gene_name,
target_hgnc_symbol: link?.geneB_external_gene_name,
brain_regions: [link.brainRegion],
};
} else if (!links[key].brain_regions.includes(link['brainRegion'])) {
links[key].brain_regions.push(link['brainRegion']);
}
});
}

response.links = Object.values(links).sort((a: any, b: any) => {
return a.brain_regions?.length - b.brain_regions?.length;
});

response.links.forEach((link: any) => {
link.brain_regions.sort();

['source', 'target'].forEach((key: any) => {
if (!nodes[link[key]]) {
nodes[link[key]] = {
ensembl_gene_id: link[key],
hgnc_symbol: link[key + '_hgnc_symbol'],
brain_regions: link.brain_regions,
};
} else {
link.brain_regions.forEach((brainRegion: any) => {
if (!nodes[link[key]].brain_regions.includes(brainRegion)) {
nodes[link[key]].brain_regions.push(brainRegion);
}
});
}
});
});

response.nodes = Object.values(nodes)
.sort((a: any, b: any) => {
return a.brain_regions?.length - b.brain_regions?.length;
})
.reverse();

response.nodes.forEach((node: any, i: number) => {
node.brain_regions.sort();

if (node.brain_regions.length < response.min) {
response.min = node.brain_regions.length;
}

if (node.brain_regions.length > response.max) {
response.max = node.brain_regions.length;
}

// Insert current node to the beginning of the array
if (node.ensembl_gene_id === gene.ensembl_gene_id) {
const currentNode = node;
response.nodes.splice(i, 1);
response.nodes.unshift(currentNode);
}
});

return response;
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ <h3>
these genes are associated with AD.
</div>
<div>
<i (click)="navigateToSimilarGenes()" class="fa fa-angle-right"></i>
<fa-icon (click)="navigateToSimilarGenes()" [icon]="faAngleRight"></fa-icon>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* stylelint-disable no-descending-specificity */

@use 'sass:map';
@import 'libs/agora/styles/src/lib/variables';
@import 'libs/agora/styles/src/lib/mixins';
@use 'libs/agora/styles/src/lib/variables';
@use 'libs/agora/styles/src/lib/mixins';

.gene-network {
min-height: 600px;
Expand Down Expand Up @@ -37,7 +37,7 @@
display: block;
width: 15px;
height: 15px;
background-color: map.get($gene-network-colors, 'selected');
background-color: map.get(variables.$gene-network-colors, 'selected');
border-radius: 50%;
cursor: pointer;
}
Expand All @@ -54,7 +54,7 @@
height: 4px;
top: 6px;
left: 0;
background-color: map.get($gene-network-colors, 'selected');
background-color: map.get(variables.$gene-network-colors, 'selected');
}
}

Expand Down Expand Up @@ -82,7 +82,7 @@

svg {
margin-right: 10px;
color: map.get($gene-network-colors, 'main');
color: map.get(variables.$gene-network-colors, 'main');
}

&:not(:first-child) {
Expand All @@ -97,19 +97,19 @@
}

&:nth-child(2)::before {
background-color: map.get($gene-network-colors, 'selected');
background-color: map.get(variables.$gene-network-colors, 'selected');
}

&:nth-child(3)::before {
background-color: map.get($gene-network-colors, '2-3');
background-color: map.get(variables.$gene-network-colors, '2-3');
}

&:nth-child(4)::before {
background-color: map.get($gene-network-colors, '4-5');
background-color: map.get(variables.$gene-network-colors, '4-5');
}

&:nth-child(5)::before {
background-color: map.get($gene-network-colors, '>6');
background-color: map.get(variables.$gene-network-colors, '>6');
}
}
}
Expand Down Expand Up @@ -157,7 +157,7 @@

.gene-network-selected-similar-list {
a {
@include link;
@include mixins.link;

display: inline-block;
font-weight: 700;
Expand Down Expand Up @@ -199,50 +199,79 @@
.network-chart {
svg {
.network-chart-link {
stroke: map.get($gene-network-colors, '>6');
stroke: map.get(variables.$gene-network-colors, '>6');

&.edges-0,
&.edges-1 {
stroke: map.get($gene-network-colors, 'default');
stroke: map.get(variables.$gene-network-colors, 'default');
}

&.edges-2,
&.edges-3 {
stroke: map.get($gene-network-colors, '2-3');
stroke: map.get(variables.$gene-network-colors, '2-3');
}

&.edges-4,
&.edges-5 {
stroke: map.get($gene-network-colors, '4-5');
stroke: map.get(variables.$gene-network-colors, '4-5');
}
}

.network-chart-node {
fill: map.get($gene-network-colors, '>6');
fill: map.get(variables.$gene-network-colors, '>6');
cursor: pointer;

&.edges-0,
&.edges-1 {
fill: map.get($gene-network-colors, 'default');
fill: map.get(variables.$gene-network-colors, 'default');
}

&.edges-2,
&.edges-3 {
fill: map.get($gene-network-colors, '2-3');
fill: map.get(variables.$gene-network-colors, '2-3');
}

&.edges-4,
&.edges-5 {
fill: map.get($gene-network-colors, '4-5');
fill: map.get(variables.$gene-network-colors, '4-5');
}

&.main {
fill: map.get($gene-network-colors, 'main');
fill: map.get(variables.$gene-network-colors, 'main');
}

&.selected {
fill: map.get($gene-network-colors, 'selected');
fill: map.get(variables.$gene-network-colors, 'selected');
}
}
}
}

.row {
display: flex;
flex-wrap: nowrap;
max-width: 100%;

.col-lg-8,
.col-lg-4 {
padding: 0 12px;
flex: 0 0 auto;
}

.col-lg-8 {
width: 66.67%;
}

.col-lg-4 {
width: 33.33%;
}

@media (max-width: variables.$lg-breakpoint) {
flex-wrap: wrap;

.col-lg-8,
.col-lg-4 {
flex: 1 1 100%;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { CommonModule } from '@angular/common';
import { Component, Input, ViewEncapsulation, inject } from '@angular/core';
import { Router } from '@angular/router';

import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
import {
Gene,
GenesService,
Expand All @@ -18,7 +20,7 @@ import { TooltipModule } from 'primeng/tooltip';

@Component({
selector: 'agora-gene-network',
imports: [CommonModule, NetworkChartComponent, TooltipModule],
imports: [CommonModule, FontAwesomeModule, NetworkChartComponent, TooltipModule],
providers: [GenesService],
templateUrl: './gene-network.component.html',
styleUrls: ['./gene-network.component.scss'],
Expand All @@ -44,6 +46,8 @@ export class GeneNetworkComponent {
filters: number[] = [];
selectedFilter = 1;

faAngleRight = faAngleRight;

init() {
if (!this._gene?.similar_genes_network?.nodes?.length) {
this.data = undefined;
Expand Down Expand Up @@ -87,7 +91,7 @@ export class GeneNetworkComponent {
}

onNodeClick(node: NetworkChartNode) {
this.geneService.getGene(node.id).subscribe((gene: any) => {
this.geneService.getGene(node.id).subscribe((gene) => {
this.selectedGene = gene;
});
}
Expand Down
Loading

0 comments on commit afd7caa

Please sign in to comment.