Skip to content

Commit

Permalink
Merge pull request #78 from daisybio/dev
Browse files Browse the repository at this point in the history
Add latest dev features
  • Loading branch information
nictru authored Jan 24, 2025
2 parents 22e9bbe + e2295be commit 83da4e4
Show file tree
Hide file tree
Showing 24 changed files with 1,221 additions and 745 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@ngx-dropzone/cdk": "^19.0.0",
"@ngx-dropzone/material": "^19.0.0",
"@sigma/node-square": "^3.0.0",
"@visa-ge/ng-igv": "^0.0.9",
"file-saver": "^2.0.5",
"graphology": "^0.25.4",
"graphology-layout-force": "^0.2.4",
Expand Down
6 changes: 5 additions & 1 deletion src/app/components/gene-modal/gene-modal.component.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h1 mat-dialog-title>{{ gene.gene_symbol || gene.ensg_number }}</h1>
<mat-dialog-content>
<mat-tab-group>
<mat-tab-group [(selectedIndex)]="activeTab$" preserveContent>
<mat-tab label="General information">
<mat-accordion>
<mat-expansion-panel expanded>
Expand Down Expand Up @@ -125,6 +125,10 @@ <h1 mat-dialog-title>{{ gene.gene_symbol || gene.ensg_number }}</h1>

</mat-tab>
}
<mat-tab label="Genome view">
<igv [location]="this.location$()" [reference]="IGV_REFGENOME" [refresh]="activeTab$()"
[tracks]="miRNAtracks$()"></igv>
</mat-tab>

</mat-tab-group>
</mat-dialog-content>
Expand Down
183 changes: 139 additions & 44 deletions src/app/components/gene-modal/gene-modal.component.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import {AfterViewInit, Component, effect, inject, model, resource, ResourceRef, viewChild} from '@angular/core';
import {Gene, GOTerm} from "../../interfaces";
import {MAT_DIALOG_DATA, MatDialog, MatDialogModule} from "@angular/material/dialog";
import {MatButtonModule} from "@angular/material/button";
import {VersionsService} from "../../services/versions.service";
import {BackendService} from "../../services/backend.service";
import {MatTabsModule} from "@angular/material/tabs";
import {MatExpansionModule} from "@angular/material/expansion";
import {MatTableDataSource, MatTableModule} from "@angular/material/table";
import {MatPaginator, MatPaginatorModule} from "@angular/material/paginator";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatInputModule} from "@angular/material/input";
import {FormsModule} from "@angular/forms";
import {MatChip, MatChipSet} from "@angular/material/chips";
import {MatProgressSpinner} from "@angular/material/progress-spinner";
import {TranscriptModalComponent} from "../transcript-modal/transcript-modal.component";
import {AS_DESCRIPTIONS} from "../../constants";
import {MatTooltip} from "@angular/material/tooltip";
import {
AfterViewInit,
Component,
computed,
effect,
inject,
model,
resource,
ResourceRef,
Signal,
viewChild,
} from '@angular/core';
import { Gene, GOTerm } from '../../interfaces';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { VersionsService } from '../../services/versions.service';
import { BackendService } from '../../services/backend.service';
import { MatTabsModule } from '@angular/material/tabs';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
import { MatChip, MatChipSet } from '@angular/material/chips';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { AS_DESCRIPTIONS, IGV_REFGENOME } from '../../constants';
import { MatTooltip } from '@angular/material/tooltip';
import { Igv, Location, Track } from '@visa-ge/ng-igv';
import { ModalsService } from '../modals-service/modals.service';
import { BrowseService } from '../../services/browse.service';

interface ASEntry {
enst: string;
Expand All @@ -37,69 +50,151 @@ interface ASEntry {
MatChipSet,
MatChip,
MatProgressSpinner,
MatTooltip
MatTooltip,
Igv,
],
templateUrl: './gene-modal.component.html',
styleUrl: './gene-modal.component.scss'
styleUrl: './gene-modal.component.scss',
})
export class GeneModalComponent implements AfterViewInit {
goPaginator = viewChild.required<MatPaginator>('goPaginator');
asPaginator = viewChild.required<MatPaginator>('asPaginator');
goColumns = ['symbol', 'description']
asColumns = ['enst', 'events']
goFilter = model<string>('')
dialog = inject(MatDialog);
readonly asDescriptions = AS_DESCRIPTIONS
goColumns = ['symbol', 'description'];
asColumns = ['enst', 'events'];
goFilter = model<string>('');
readonly asDescriptions = AS_DESCRIPTIONS;
modalsService = inject(ModalsService);

browseService = inject(BrowseService);
readonly disease$ = this.browseService.disease$;
readonly gene = inject<Gene>(MAT_DIALOG_DATA);
readonly versionsService = inject(VersionsService);
readonly backend = inject(BackendService);
readonly version$ = this.versionsService.versionReadOnly();
readonly isOpeningTranscript = model<boolean>(false);
readonly activeTab$ = model<number>(0);

goDatasource = new MatTableDataSource<GOTerm>();
asDatasource = new MatTableDataSource<ASEntry>();

edges$ = this.browseService.getEdgesForNode(this.gene);
miRNAs$ = resource({
request: computed(() => {
return {
edges: this.edges$(),
disease: this.disease$(),
version: this.version$(),
};
}),
loader: async (param) => {
const edges = param.request.edges;
const disease = param.request.disease;
const version = param.request.version;
if (!disease) {
return Promise.resolve([]);
}
const miRNAs$ = edges.map((edge) =>
this.backend
.getMiRNAs(
version,
disease,
BrowseService.getInteractionIDs(edge),
'gene',
)
.then((res) => res.map((mirna) => mirna.mirna.hs_nr)),
);
return (await Promise.all(miRNAs$))
.flat()
.filter((miRNA, i, arr) => arr.indexOf(miRNA) === i);
},
});

miRNAtracks$ = computed((): Track[] => {
const miRNAs = this.miRNAs$.value();
if (!miRNAs) {
return [];
}
return miRNAs.map((miRNA) => {
return {
name: miRNA,
url: `https://exbio.wzw.tum.de/sponge-files/miRNA_bed_files/${miRNA}.bed.gz`,
format: 'bed',
type: 'annotation',
height: 30,
displayMode: 'SQUISHED',
indexed: false,
};
});
});

readonly geneInfo$ = resource({
request: this.version$,
loader: async (version) => this.backend.getGeneInfo(version.request, this.gene.ensg_number).then(info => info[0])
})
loader: async (version) =>
this.backend
.getGeneInfo(version.request, this.gene.ensg_number)
.then((info) => info[0]),
});

readonly goTerms$ = resource({
request: this.version$,
loader: async (version) => this.backend.getGOterms(version.request, this.gene.gene_symbol)
})
loader: async (version) =>
this.backend.getGOterms(version.request, this.gene.gene_symbol),
});

readonly hallmarks$ = resource({
request: this.version$,
loader: async (version) => this.backend.getHallmark(version.request, this.gene.gene_symbol)
})
loader: async (version) =>
this.backend.getHallmark(version.request, this.gene.gene_symbol),
});

readonly wikipathways$ = resource({
request: this.version$,
loader: async (version) => this.backend.getWikiPathways(version.request, this.gene.gene_symbol)
})
loader: async (version) =>
this.backend.getWikiPathways(version.request, this.gene.gene_symbol),
});

readonly transcripts$: ResourceRef<ASEntry[]> = resource({
request: this.version$,
loader: async (version) => {
const transcripts = await this.backend.getGeneTranscripts(version.request, this.gene.ensg_number);
const asEvents = await this.backend.getAlternativeSplicingEvents(transcripts);
const transcripts = await this.backend.getGeneTranscripts(
version.request,
this.gene.ensg_number,
);
const asEvents =
await this.backend.getAlternativeSplicingEvents(transcripts);
const transcriptEvents = asEvents.reduce((acc, event) => {
const enst = event.transcript.enst_number;
if (!acc.has(enst)) {
acc.set(enst, new Set<string>());
}
acc.get(enst)!.add(event.event_type);
return acc;
}, new Map<string, Set<string>>())
}, new Map<string, Set<string>>());

return transcripts.map(t => ({
return transcripts.map((t) => ({
enst: t,
events: Array.from(transcriptEvents.get(t) ?? [])
events: Array.from(transcriptEvents.get(t) ?? []),
}));
},
});

readonly location$: Signal<Location> = computed(() => {
const geneInfo = this.geneInfo$.value();
if (!geneInfo) {
return {
chr: 'all',
};
} else {
return {
chr: geneInfo.chromosome_name,
range: {
start: geneInfo.start_pos,
end: geneInfo.end_pos,
},
};
}
})
});
protected readonly IGV_REFGENOME = IGV_REFGENOME;

constructor() {
effect(() => {
Expand All @@ -112,7 +207,7 @@ export class GeneModalComponent implements AfterViewInit {

effect(() => {
this.asDatasource.data = this.transcripts$.value() ?? [];
})
});
}

ngAfterViewInit(): void {
Expand All @@ -123,10 +218,10 @@ export class GeneModalComponent implements AfterViewInit {
async openTranscript(enst: string) {
if (this.isOpeningTranscript()) return;
this.isOpeningTranscript.set(true);
const transcript = (await this.backend.getTranscriptInfo(this.version$(), enst))[0];
this.dialog.open(TranscriptModalComponent, {
data: transcript
});
const transcript = (
await this.backend.getTranscriptInfo(this.version$(), enst)
)[0];
this.modalsService.openNodeDialog(transcript);
this.isOpeningTranscript.set(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

<ng-container matColumnDef="mscor">
<th *matHeaderCellDef mat-header-cell mat-sort-header>
<app-info title="MScor">SPONGE computes multiple miRNA sensitivity correlation values. Note that this is a
<app-info title="mscor">SPONGE computes multiple miRNA sensitivity correlation values. Note that this is a
generalization of sensitivity correlation as defined by <a href="https://www.ncbi.nlm.nih.gov/pubmed/25033876">Paci
et al.</a>. These values capture the joint contribution
of several miRNAs on the ceRNA regulation of two genes while accounting for their cross-correlation.
Expand All @@ -51,7 +51,7 @@

<ng-container matColumnDef="padj">
<th *matHeaderCellDef mat-header-cell mat-sort-header>
<app-info title="Adj. p-Value">SPONGE computes a null model to calculate empirical p-values for the ceRNA
<app-info title="Adj. p-value">SPONGE computes a null model to calculate empirical p-values for the ceRNA
interactions. We sampled 1,000,000 datasets to closely estimate the p-values. The interactions were then
FDR-corrected and filtered with a p-value cut-off of 0.01.
</app-info>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,13 @@ import {
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatSort, MatSortHeader } from '@angular/material/sort';
import { GeneModalComponent } from '../gene-modal/gene-modal.component';
import { MatDialog } from '@angular/material/dialog';
import { MatButton } from '@angular/material/button';
import { MatTooltip } from '@angular/material/tooltip';
import { BrowseService } from '../../services/browse.service';
import { TranscriptModalComponent } from '../transcript-modal/transcript-modal.component';
import { InteractionModalComponent } from '../interaction-modal/interaction-modal.component';
import { capitalize } from 'lodash';
import { InfoComponent } from '../info/info.component';
import katex from 'katex';
import { ModalsService } from '../modals-service/modals.service';

@Component({
selector: 'app-interactions-table',
Expand All @@ -46,9 +43,9 @@ import katex from 'katex';
export class InteractionsTableComponent implements AfterViewInit {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
modalsService = inject(ModalsService);
level$ = input<'gene' | 'transcript'>();
interactions$ = input<(GeneInteraction | TranscriptInteraction)[]>();
readonly dialog = inject(MatDialog);
mscorEquation$ = viewChild<ElementRef<HTMLSpanElement>>('mscorEquation');
columns = ['name_1', 'name_2', 'mirna', 'correlation', 'mscor', 'padj'];
dataSource$ = computed(() => {
Expand Down Expand Up @@ -98,20 +95,10 @@ export class InteractionsTableComponent implements AfterViewInit {
}

openMiRNADialog(interaction: GeneInteraction | TranscriptInteraction) {
this.dialog.open(InteractionModalComponent, {
data: interaction,
});
this.modalsService.openMiRNADialog(interaction);
}

openDialog(entity: Gene | Transcript) {
if ('ensg_number' in entity) {
this.dialog.open(GeneModalComponent, {
data: entity,
});
} else {
this.dialog.open(TranscriptModalComponent, {
data: entity,
});
}
this.modalsService.openNodeDialog(entity);
}
}
16 changes: 16 additions & 0 deletions src/app/components/modals-service/modals-service.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { ModalsService } from './modals.service';

describe('ModalsServiceService', () => {
let service: ModalsService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ModalsService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
Loading

0 comments on commit 83da4e4

Please sign in to comment.