From c70757f072d8a6beb6eb815eb3d8c7b067d0f7f6 Mon Sep 17 00:00:00 2001 From: Kim Rutherford Date: Mon, 26 Feb 2024 11:58:53 +1300 Subject: [PATCH 1/4] Move deletion and WT to top of gene alleles page Refs pombase/website#1255 --- src/app/app-routing.module.ts | 4 +- src/app/app.module.ts | 2 + .../gene-allele-list.component.html | 82 ++----- .../gene-allele-list.component.ts | 211 ++---------------- .../gene-alleles-page.component.css | 7 + .../gene-alleles-page.component.html | 31 +++ .../gene-alleles-page.component.spec.ts | 23 ++ .../gene-alleles-page.component.ts | 75 +++++++ src/styles.css | 2 +- 9 files changed, 179 insertions(+), 258 deletions(-) create mode 100644 src/app/gene-alleles-page/gene-alleles-page.component.css create mode 100644 src/app/gene-alleles-page/gene-alleles-page.component.html create mode 100644 src/app/gene-alleles-page/gene-alleles-page.component.spec.ts create mode 100644 src/app/gene-alleles-page/gene-alleles-page.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 52af528ff..219cab2bf 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule, NoPreloading } from '@angular/router'; import { GeneDetailsComponent } from './gene-details/gene-details.component'; -import { GeneAlleleListComponent } from './gene-allele-list/gene-allele-list.component'; import { GeneProteinFeaturesComponent } from './gene-protein-features/gene-protein-features.component'; import { GenotypeDetailsComponent } from './genotype-details/genotype-details.component'; import { AlleleDetailsComponent } from './allele-details/allele-details.component'; @@ -25,13 +24,14 @@ import { IdentifierMapperResultsComponent } from './identifier-mapper-results/id import { getAppConfig } from './config'; import { RefGenesViewComponent } from './ref-genes-view/ref-genes-view.component'; import { CurationStatsComponent } from './curation-stats/curation-stats.component'; +import { GeneAllelesPageComponent } from './gene-alleles-page/gene-alleles-page.component'; const routes: Routes = [ { path: 'gene/:uniquename', component: GeneDetailsComponent, data: { } }, - { path: 'gene_alleles/:uniquename', component: GeneAlleleListComponent, + { path: 'gene_alleles/:uniquename', component: GeneAllelesPageComponent, data: { } }, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8eca84f7a..ff45c7116 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -157,6 +157,7 @@ import { RefGenesViewComponent } from './ref-genes-view/ref-genes-view.component import { CurationStatsComponent } from './curation-stats/curation-stats.component'; import { AnnotationTableWidgetsComponent } from './annotation-table-widgets/annotation-table-widgets.component'; import { StatsSummaryComponent } from './stats-summary/stats-summary.component'; +import { GeneAllelesPageComponent } from './gene-alleles-page/gene-alleles-page.component'; @Pipe({ name: 'safeUrl' }) export class SafeUrlPipe implements PipeTransform { @@ -280,6 +281,7 @@ export class SafeUrlPipe implements PipeTransform { CurationStatsComponent, AnnotationTableWidgetsComponent, StatsSummaryComponent, + GeneAllelesPageComponent, ], imports: [ BrowserModule, diff --git a/src/app/gene-allele-list/gene-allele-list.component.html b/src/app/gene-allele-list/gene-allele-list.component.html index 3ce6c38ed..f24aab941 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.html +++ b/src/app/gene-allele-list/gene-allele-list.component.html @@ -1,85 +1,43 @@ -
- - - -
- Show genotypes: - All - None -
- - - - - - - - - - - + - - - - - - - - - + + +
- + Name - - Name + + Name - + Description - - Description + + Description - + Type - - Type + + Type ExpressionGenotype
- {{allele.alleleName}} - {{allele.alleleName}} - - {{allele.alleleDescription}} - - {{allele.alleleType}} - +
+ {{allele.name}} + - Show genotypes.. - -
{{expressedAllele.expression}}
-
- -
+ {{allele.description}} + + {{allele.allele_type}} +
- -
diff --git a/src/app/gene-allele-list/gene-allele-list.component.ts b/src/app/gene-allele-list/gene-allele-list.component.ts index 689a15ef1..512fd2102 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.ts +++ b/src/app/gene-allele-list/gene-allele-list.component.ts @@ -1,119 +1,18 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; -import { GeneDetails, PombaseAPIService, GenotypeShort, - AlleleShort } from '../pombase-api.service'; -import { DeployConfigService } from '../deploy-config.service'; -import { Util } from '../shared/util'; - -class AlleleSection { - public nameAndDescription: string; - - constructor(public alleleUniquename: string, - public alleleName: string, - public alleleDescription: string, - public alleleType: string, - public expressedAlleles: Array) { - this.alleleName = Util.tidyAlleleName(alleleName); - - let description = this.alleleDescription; - - if (!description) { - description = 'unknown'; - this.alleleDescription = ''; - } - - this.nameAndDescription = name + '(' + description + ')'; - } - - expressedAlleleCount(): number { - return this.expressedAlleles.length; - } - - genotypeCount(): number { - let count = 0; - - for (const expressedAllele of this.expressedAlleles) { - count += expressedAllele.genotypes.length; - } - - return count; - } -} - -class ExpressedAlleleSection { - constructor(public expression: string, public genotypes: Array) { - - } -} - -type ExpressedAlleleMap = { [expression: string]: Array }; -type AlleleMap = { [alleleUniquename: string]: ExpressedAlleleMap }; - -function genotypeSorter(g1: GenotypeShort, g2: GenotypeShort) { - if (g1.loci.length < g2.loci.length) { - return -1; - } - if (g1.loci.length > g2.loci.length) { - return 1; - } - - if (g1.loci[0].expressed_alleles.length < g2.loci[0].expressed_alleles.length) { - return -1; - } - if (g1.loci[0].expressed_alleles.length > g2.loci[0].expressed_alleles.length) { - return 1; - } - - return g1.displayNameLong.localeCompare(g2.displayNameLong); -} +import { AlleleShort } from '../pombase-api.service'; @Component({ selector: 'app-gene-allele-list', templateUrl: './gene-allele-list.component.html', styleUrls: ['./gene-allele-list.component.css'] }) -export class GeneAlleleListComponent implements OnInit { - @Input() geneDetails: GeneDetails; +export class GeneAlleleListComponent implements OnChanges { + @Input() alleles: Array; - alleleTable: Array; sortByColumnName: string = 'type'; - genotypeVisible: { [key: string ]: boolean } = {}; - - constructor(private pombaseApiService: PombaseAPIService, - private route: ActivatedRoute, - public deployConfigService: DeployConfigService) { } - - genotypesVisible(allele: AlleleSection): boolean { - return !!this.genotypeVisible[allele.alleleUniquename]; - } - - anyVisibleGenotypes(): boolean { - return Object.keys(this.genotypeVisible).length > 0; - } - - showGenotypes(allele: AlleleSection): void { - this.genotypeVisible[allele.alleleUniquename] = true; - } - - showAllGenotypes(): void { - for (const allele of this.alleleTable) { - this.genotypeVisible[allele.alleleUniquename] = true; - } - } - - hideAllGenotypes(): void { - this.genotypeVisible = {}; - } - - alleleRowspan(allele: AlleleSection): number { - if (this.genotypesVisible(allele)) { - return allele.genotypeCount(); - } else { - return 1; - } - } + constructor() { } sortBy(columnName: 'name'|'description'|'type'): void { this.sortByColumnName = columnName; @@ -122,39 +21,40 @@ export class GeneAlleleListComponent implements OnInit { } sortTable(): void { + const nameDesc = (a: AlleleShort) => (a.name || 'unknown') + '(' + (a.name || 'unknown') + ')'; if (this.sortByColumnName == 'type') { - this.alleleTable.sort((a, b) => { - if (a.alleleType === b.alleleType) { - return a.nameAndDescription.localeCompare(b.nameAndDescription); + this.alleles.sort((a, b) => { + if (a.allele_type === b.allele_type) { + return nameDesc(a).localeCompare(nameDesc(b)); } else { - return a.alleleType.localeCompare(b.alleleType); + return a.allele_type.localeCompare(b.allele_type); } }); } else { - this.alleleTable.sort((a, b) => { + this.alleles.sort((a, b) => { let result; if (this.sortByColumnName === 'name') { - result = a.alleleName.localeCompare(b.alleleName); + result = a.name.localeCompare(b.name); } else { - if (a.alleleDescription.length == 0 && b.alleleDescription.length == 0) { + if (a.description.length == 0 && b.description.length == 0) { result = 0; } else { - if (a.alleleDescription.length == 0) { + if (a.description.length == 0) { // missing / blank description sort last result = 1; } else { - if (b.alleleDescription.length == 0) { + if (b.description.length == 0) { return -1 } else { - result = a.alleleDescription.localeCompare(b.alleleDescription); + result = a.description.localeCompare(b.description); } } } } if (result === 0) { - result = a.alleleType.localeCompare(b.alleleType); + result = a.allele_type.localeCompare(b.allele_type); } return result; @@ -163,82 +63,7 @@ export class GeneAlleleListComponent implements OnInit { } } - makeAlleleTable(): void { - this.alleleTable = []; - - const alleleExpressionGenotypeMap: AlleleMap = {}; - - const alleleMap: { [alleleUniquename: string]: AlleleShort } = {}; - - GENOTYPE: - for (const genotypeUniquename of Object.keys(this.geneDetails.genotypes_by_uniquename)) { - const genotypeShort = this.geneDetails.genotypes_by_uniquename[genotypeUniquename]; - - for (const locus of genotypeShort.loci) { - for (const expressedAllele of locus.expressed_alleles) { - const allele = expressedAllele.allele; - - if (allele.gene_uniquename !== this.geneDetails.uniquename) { - continue; - } - - if (!alleleMap[allele.uniquename]) { - alleleMap[allele.uniquename] = allele; - } - - if (!alleleExpressionGenotypeMap[allele.uniquename]) { - alleleExpressionGenotypeMap[allele.uniquename] = {}; - } - - const expressedAlleleMap = alleleExpressionGenotypeMap[allele.uniquename]; - - if (!expressedAlleleMap[expressedAllele.expression]) { - expressedAlleleMap[expressedAllele.expression] = []; - } - - expressedAlleleMap[expressedAllele.expression].push(genotypeShort); - } - } - } - - for (const [alleleUniquename, expressedAlleleMap] of Object.entries(alleleExpressionGenotypeMap)) { - const expressedAlleleSections = []; - for (const [expression, genotypes] of Object.entries(expressedAlleleMap)) { - genotypes.sort(genotypeSorter); - expressedAlleleSections.push(new ExpressedAlleleSection(expression, genotypes)); - } - - expressedAlleleSections.sort((a, b) => - a.expression.localeCompare(b.expression) - ); - - const alleleShort = alleleMap[alleleUniquename]; - - const alleleName = alleleShort.name; - const alleleDescription = alleleShort.description; - const alleleType = alleleShort.allele_type; - - const alleleSection = - new AlleleSection(alleleUniquename, alleleName, alleleDescription, alleleType, - expressedAlleleSections); - this.alleleTable.push(alleleSection); - } - + ngOnChanges(changes: SimpleChanges): void { this.sortTable(); } - - ngOnInit(): void { - this.route.params.forEach((params: Params) => { - if (params['uniquename'] !== undefined) { - let uniquename = params['uniquename']; - - this.pombaseApiService.getGene(uniquename) - .then(geneDetails => { - this.geneDetails = geneDetails; - - this.makeAlleleTable(); - }); - } - }) - } } diff --git a/src/app/gene-alleles-page/gene-alleles-page.component.css b/src/app/gene-alleles-page/gene-alleles-page.component.css new file mode 100644 index 000000000..9ad876a35 --- /dev/null +++ b/src/app/gene-alleles-page/gene-alleles-page.component.css @@ -0,0 +1,7 @@ +.allele-section { + padding-bottom: 1em; +} + +.deletion, .wild-type { + font-size: 150%; +} \ No newline at end of file diff --git a/src/app/gene-alleles-page/gene-alleles-page.component.html b/src/app/gene-alleles-page/gene-alleles-page.component.html new file mode 100644 index 000000000..c7989dc88 --- /dev/null +++ b/src/app/gene-alleles-page/gene-alleles-page.component.html @@ -0,0 +1,31 @@ +
+
+
+ + + +
+ Deletion allele: {{deletionAlleles[0].name}} +
+ +
+ Wild type alleles: + +
+
+

+ Other alleles: +

+ +
+
diff --git a/src/app/gene-alleles-page/gene-alleles-page.component.spec.ts b/src/app/gene-alleles-page/gene-alleles-page.component.spec.ts new file mode 100644 index 000000000..c572c6e57 --- /dev/null +++ b/src/app/gene-alleles-page/gene-alleles-page.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GeneAllelesPageComponent } from './gene-alleles-page.component'; + +describe('GeneAllelesPageComponent', () => { + let component: GeneAllelesPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ GeneAllelesPageComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(GeneAllelesPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/gene-alleles-page/gene-alleles-page.component.ts b/src/app/gene-alleles-page/gene-alleles-page.component.ts new file mode 100644 index 000000000..96e559ef0 --- /dev/null +++ b/src/app/gene-alleles-page/gene-alleles-page.component.ts @@ -0,0 +1,75 @@ +import { Component, OnInit, Input } from '@angular/core'; + +import { ActivatedRoute, Params } from '@angular/router'; +import { GeneDetails, PombaseAPIService, AlleleShort } from '../pombase-api.service'; +import { DeployConfigService } from '../deploy-config.service'; + +@Component({ + selector: 'app-gene-alleles-page', + templateUrl: './gene-alleles-page.component.html', + styleUrls: ['./gene-alleles-page.component.css'] +}) +export class GeneAllelesPageComponent implements OnInit { + @Input() geneDetails: GeneDetails; + + deletionAlleles: Array = []; + wildtypeAlleles: Array = []; + otherAlleles: Array = []; + + constructor(private pombaseApiService: PombaseAPIService, + private route: ActivatedRoute, + public deployConfigService: DeployConfigService) { } + + makeAlleleTables(): void { + this.deletionAlleles = []; + this.wildtypeAlleles = []; + this.otherAlleles = []; + + const seenAlleles: Set = new Set(); + + for (const genotypeUniquename of Object.keys(this.geneDetails.genotypes_by_uniquename)) { + const genotypeShort = this.geneDetails.genotypes_by_uniquename[genotypeUniquename]; + + for (const locus of genotypeShort.loci) { + for (const expressedAllele of locus.expressed_alleles) { + const allele = expressedAllele.allele; + + if (allele.gene_uniquename !== this.geneDetails.uniquename) { + continue; + } + + if (seenAlleles.has(allele.uniquename)) { + continue; + } + + seenAlleles.add(allele.uniquename); + + if (allele.allele_type == 'deletion') { + this.deletionAlleles.push(allele); + } else { + if (allele.allele_type == 'wild_type') { + this.wildtypeAlleles.push(allele); + } else { + this.otherAlleles.push(allele); + } + } + } + } + } + } + + ngOnInit(): void { + this.route.params.forEach((params: Params) => { + if (params['uniquename'] !== undefined) { + let uniquename = params['uniquename']; + + this.pombaseApiService.getGene(uniquename) + .then(geneDetails => { + this.geneDetails = geneDetails; + + this.makeAlleleTables(); + }); + } + }) + } +} diff --git a/src/styles.css b/src/styles.css index 37a7d889b..a4cf83e79 100644 --- a/src/styles.css +++ b/src/styles.css @@ -202,7 +202,7 @@ html body hr { } ul { - padding-left: 25px; + padding-left: 1.5em; } .app-link { From ebfd1b35ff3123c2e31b6f731c2f83fb373ba1b6 Mon Sep 17 00:00:00 2001 From: Kim Rutherford Date: Mon, 26 Feb 2024 12:39:07 +1300 Subject: [PATCH 2/4] Add missing "View genotypes ..." on alleles page Refs pombase/website#1255 --- src/app/gene-allele-list/gene-allele-list.component.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/gene-allele-list/gene-allele-list.component.html b/src/app/gene-allele-list/gene-allele-list.component.html index f24aab941..d814ac8e8 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.html +++ b/src/app/gene-allele-list/gene-allele-list.component.html @@ -39,5 +39,9 @@ {{allele.allele_type}} + + + View genotypes.. + From 54e0dd7ece62113bf88fa9776df211b24b2b2545 Mon Sep 17 00:00:00 2001 From: Kim Rutherford Date: Mon, 26 Feb 2024 13:23:11 +1300 Subject: [PATCH 3/4] Add tooltips on alleles of gene pages Refs pombase/website#1255 --- src/app/gene-allele-list/gene-allele-list.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/gene-allele-list/gene-allele-list.component.html b/src/app/gene-allele-list/gene-allele-list.component.html index d814ac8e8..136990e45 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.html +++ b/src/app/gene-allele-list/gene-allele-list.component.html @@ -30,18 +30,18 @@ - {{allele.name}} + {{allele.name}} - {{allele.description}} {{allele.allele_type}} - - View genotypes.. + View genotypes.. From 1c70b6891da6dcb91a7124910336721ccfaa0393 Mon Sep 17 00:00:00 2001 From: Kim Rutherford Date: Mon, 26 Feb 2024 14:12:22 +1300 Subject: [PATCH 4/4] Wrap the name and desc. on alleles of gene page Refs pombase/website#1255 --- .../gene-allele-list.component.css | 4 ++ .../gene-allele-list.component.html | 10 ++--- .../gene-allele-list.component.ts | 42 ++++++++++++++----- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/app/gene-allele-list/gene-allele-list.component.css b/src/app/gene-allele-list/gene-allele-list.component.css index cede4118e..637733b9d 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.css +++ b/src/app/gene-allele-list/gene-allele-list.component.css @@ -34,3 +34,7 @@ td { padding-left: 0.5em; border-left: 2px solid black; } + +.view-genotype { + white-space: nowrap; +} diff --git a/src/app/gene-allele-list/gene-allele-list.component.html b/src/app/gene-allele-list/gene-allele-list.component.html index 136990e45..d1a2c6b73 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.html +++ b/src/app/gene-allele-list/gene-allele-list.component.html @@ -28,20 +28,20 @@ - + {{allele.name}} + routerLink="/allele/{{allele.uniquename}}"> - {{allele.description}} + - {{allele.allele_type}} + {{allele.alleleType}} View genotypes.. + class="view-genotype" routerLink="/allele/{{allele.uniquename}}">View genotypes.. diff --git a/src/app/gene-allele-list/gene-allele-list.component.ts b/src/app/gene-allele-list/gene-allele-list.component.ts index 512fd2102..8cd4dc77c 100644 --- a/src/app/gene-allele-list/gene-allele-list.component.ts +++ b/src/app/gene-allele-list/gene-allele-list.component.ts @@ -2,6 +2,21 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { AlleleShort } from '../pombase-api.service'; +class DisplayAllele { + displayName: string; + constructor(public uniquename: string, + public name: string, + public description: string, + public alleleType: string) { + this.displayName = this.name.replace(/,/g, ',​'); + this.description = this.description.replace(/,/g, ',​'); + } + + nameDescription(): string { + return (this.name || 'unknown') + '(' + (this.name || 'unknown') + ')'; + } +} + @Component({ selector: 'app-gene-allele-list', templateUrl: './gene-allele-list.component.html', @@ -10,6 +25,8 @@ import { AlleleShort } from '../pombase-api.service'; export class GeneAlleleListComponent implements OnChanges { @Input() alleles: Array; + displayAlleles: Array = []; + sortByColumnName: string = 'type'; constructor() { } @@ -17,21 +34,25 @@ export class GeneAlleleListComponent implements OnChanges { sortBy(columnName: 'name'|'description'|'type'): void { this.sortByColumnName = columnName; - this.sortTable(); + this.makeTable(); } - sortTable(): void { - const nameDesc = (a: AlleleShort) => (a.name || 'unknown') + '(' + (a.name || 'unknown') + ')'; + makeTable(): void { + this.displayAlleles = + this.alleles.map(a => { + return new DisplayAllele(a.uniquename, a.name, a.description, a.allele_type); + }); + if (this.sortByColumnName == 'type') { - this.alleles.sort((a, b) => { - if (a.allele_type === b.allele_type) { - return nameDesc(a).localeCompare(nameDesc(b)); + this.displayAlleles.sort((a, b) => { + if (a.alleleType === b.alleleType) { + return a.nameDescription().localeCompare(b.nameDescription()); } else { - return a.allele_type.localeCompare(b.allele_type); + return a.alleleType.localeCompare(b.alleleType); } }); } else { - this.alleles.sort((a, b) => { + this.displayAlleles.sort((a, b) => { let result; if (this.sortByColumnName === 'name') { @@ -54,16 +75,15 @@ export class GeneAlleleListComponent implements OnChanges { } if (result === 0) { - result = a.allele_type.localeCompare(b.allele_type); + result = a.alleleType.localeCompare(b.alleleType); } return result; }) - } } ngOnChanges(changes: SimpleChanges): void { - this.sortTable(); + this.makeTable(); } }