Skip to content

Commit

Permalink
Merge pull request #262 from TheJacksonLaboratory/feature/translations
Browse files Browse the repository at this point in the history
Feature/translations
  • Loading branch information
iimpulse authored Sep 13, 2023
2 parents 185dad9 + 19d5bd4 commit 9d46eb0
Show file tree
Hide file tree
Showing 18 changed files with 49,217 additions and 59 deletions.
4 changes: 2 additions & 2 deletions client/package-lock.json

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

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hpo-web",
"version": "1.8.11",
"version": "1.8.12",
"license": "MIT",
"scripts": {
"ng": "ng",
Expand Down
7 changes: 5 additions & 2 deletions client/src/app/browser/browser.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { TranslatePipe } from '../shared/pipes/translate.pipe';
import {BrowserRoutingModule} from './browser-routing.module';
// Material Modules
import {GlobalMaterialModules} from '../shared/modules/global.module';
// Services
import {SearchService} from '../shared/search/service/search.service';
import {LanguageService} from './services/language/language.service';
import {TermService} from './services/term/term.service';
import {GeneService} from './services/gene/gene.service';
import {DiseaseService} from './services/disease/disease.service';
import {OntologyService} from './services/ontology/ontology.service';
// Components
import {TermComponent} from './pages/term/term.component';
import {DiseaseComponent} from './pages/disease/disease.component';
Expand All @@ -30,10 +33,10 @@ import {ProfileSearchComponent} from './pages/profile-search/profile-search.comp
GlobalMaterialModules,
ExtrasModule
],
providers: [SearchService, TermService, GeneService, DiseaseService, DialogService],
providers: [SearchService, TermService, GeneService, DiseaseService, DialogService, OntologyService],
declarations: [TermComponent, DiseaseComponent,
GeneComponent,
SearchResultsComponent, DialogExcelDownloadComponent, ProfileSearchComponent]
SearchResultsComponent, DialogExcelDownloadComponent, ProfileSearchComponent, TranslatePipe]
})
export class BrowserHPOModule {
}
13 changes: 13 additions & 0 deletions client/src/app/browser/models/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface Term {
treeCountWidth?: number;
treeMargin?: number;
pubmedXrefs: Array<any>;
translations?: Translation[];
}

export interface TermTree {
Expand Down Expand Up @@ -133,3 +134,15 @@ export interface TeamMember {
link?: string;
alumni?: boolean;
}


export interface Translation extends Language {
id?: string;
name: string;
status: string;
}

export interface Language {
language: string;
language_long: string;
}
27 changes: 27 additions & 0 deletions client/src/app/browser/pages/term/term.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,30 @@
color: lightgray;
display: inline-block;
}

.translation-chip {
background-color: #f1f0f0;
border-radius: 5px;
}

img.mat-chip-avatar::before {
width: 24px;
background: #4f8aa2;
height: 24px;
border-radius: 50%;
justify-content: center;
align-items: center;
display: flex;
overflow: hidden;
object-fit: cover;
content: ' ';
}

.translate-btn {
color: white;
margin: 0 0 0 .5rem;
}

.translate-btn .material-icons {
font-size: 18px;
}
53 changes: 33 additions & 20 deletions client/src/app/browser/pages/term/term.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ <h3 class="card-title">Hierarchy</h3>
<span class="tree-counts" [ngStyle]="{'margin-left': '-100px', 'margin-right': '5px'}"
matTooltip="{{parent.childrenCount}}" *ngIf="parent.childrenCount != 0"
matTooltipPosition="above"> </span>
<a class="link" routerLink="../{{parent.ontologyId}}">{{parent.name}}</a>
<a class="link" routerLink="../{{parent.ontologyId}}">{{parent.name | translate: parent.translations: selectedLanguage.language}}</a>
</li>
<li>
<div class="term">
<span class="tree-counts" [ngStyle]="{'margin-left': '-115px'}"
*ngIf="treeData.termCount != 0" matTooltip="{{treeData.termCount}}"
matTooltipPosition="above"> </span>
<strong>{{term.name}}</strong>
<strong>{{term.name | translate: term.translations: selectedLanguage.language}}</strong>
</div>
<ul>
<li *ngFor="let child of treeData.children" class="children">
<span class="tree-counts" [ngStyle]="setTreeStyles(child)" *ngIf="child.childrenCount != 0"
matTooltip="{{child.childrenCount}}" matTooltipPosition="above"> </span>
<a class="link" routerLink="../{{child.ontologyId}}">{{child.name}}</a>
<a class="link" routerLink="../{{child.ontologyId}}">{{child.name | translate: child.translations: selectedLanguage.language}}</a>
</li>
</ul>
</li>
Expand All @@ -49,38 +49,48 @@ <h3 class="card-title">Hierarchy</h3>
<mat-progress-bar class="loading-result-details" color="primary" mode="indeterminate">
</mat-progress-bar>
</div>
<div *ngIf="treeData">
<div *ngIf="treeData && term">
<div>
<span class="item-title">{{termTitle}}&nbsp;</span>
<span class="item-title">{{termTitle | translate: term.translations: selectedLanguage.language}}&nbsp;</span>
<span class="item-id" *ngIf="paramId != term.id">
<span class="crossout">{{paramId}}
</span><i class="material-icons obsolete-arrow">arrow_right_alt</i>
</span>
<span class="crossout">{{paramId}}</span>
<i class="material-icons obsolete-arrow">arrow_right_alt</i></span>
<span class="item-id">{{term.id}}</span>
<!-- <i matTooltip="Copy purl to clipboard!" matTooltipPosition="above"
(click)="copyToClipboard(term.purl)" class="material-icons copyToClipboard">content_copy</i>-->
<br>
<mat-chip-list class="translation-chips" aria-label="Language Selection" multiple="false">
<mat-chip class="translation-chip" *ngFor="let lang of languages" color="primary"
(click)="changeLanguage(lang)" [selected]="selectedLanguage.language === lang.language" [disableRipple]="true">
<img matChipAvatar src="https://unpkg.com/[email protected]/icons/{{lang.language}}.svg" alt="{{lang.language}}"/>
{{lang.language_long}}
</mat-chip>
</mat-chip-list>
</div>

<hr class="underline-hr">
<p><br><i>{{term.definition}}</i></p>
<div>
<p>
{{term.definition != "" ? term.definition : "No definition found."}}
</p>
</div>
<mat-list>
<mat-divider></mat-divider>
<mat-list-item>
<strong>Synonyms:</strong> &nbsp;&nbsp; <i
*ngFor="let item of term.synonyms; let isLast=last">{{item}}{{isLast ? '' : ', '}}</i>
<strong>Synonyms:</strong> &nbsp;&nbsp;
<i *ngFor="let item of term.synonyms; let isLast=last">{{item}}{{isLast ? '' : "\t-\t"}}</i>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngIf="term.comment">
<strong>Comment:</strong> &nbsp;&nbsp; {{term.comment}}
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngIf="term.pubmedXrefs.length > 0">
<strong>Pubmed References:</strong> &nbsp;&nbsp;
<span *ngFor="let item of term.pubmedXrefs; let isLast=last">
<a href="https://www.ncbi.nlm.nih.gov/pubmed/{{item.id}}" target="_blank"><i> {{item.whole}}</i></a>
{{isLast ? '' : ', '}}
</span>
</mat-list-item>
<!-- <mat-list-item *ngIf="term.pubmedXrefs.length > 0">-->
<!-- <strong>Pubmed References:</strong> &nbsp;&nbsp;-->
<!-- <span *ngFor="let item of term.pubmedXrefs; let isLast=last">-->
<!-- <a href="https://www.ncbi.nlm.nih.gov/pubmed/{{item.id}}" target="_blank"><i> {{item.whole}}</i></a>-->
<!-- {{isLast ? '' : ', '}}-->
<!-- </span>-->
<!-- </mat-list-item>-->
<mat-list-item *ngIf="term.xrefs.length > 0">
<strong>Cross References:</strong> &nbsp;&nbsp;
<i *ngFor="let item of term.xrefs; let isLast=last">{{item}}{{isLast ? '' : ', '}}</i>
Expand All @@ -91,12 +101,15 @@ <h3 class="card-title">Hierarchy</h3>
[disabled]="this.diseaseAssocCount == 0 && this.geneAssocCount == 0"
(click)="downloadDialog()"><span class="material-icons">get_app</span> Export Associations
</button>
<a class="translate-btn" href="https://obophenotype.github.io/hpo-translations/" mat-stroked-button color="primary" target="_blank">
<span class="material-icons">open_in_new</span> Translate your language
</a>
</div>
</mat-card-content>
</mat-card>

<!-- TABS -->
<div class="hpo-group-tab">
<div class="hpo-group-tab" *ngIf="term">
<mat-tab-group selectedIndex="0" class="term-details-tab-group mat-elevation-z6">
<!-- Disease association -->
<mat-tab label="Disease Associations">
Expand Down
107 changes: 75 additions & 32 deletions client/src/app/browser/pages/term/term.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import {ActivatedRoute, Params, Router} from '@angular/router';
import {MatTableDataSource} from '@angular/material/table';
import {MatPaginator} from '@angular/material/paginator';
import {MatSort} from '@angular/material/sort';
import {forkJoin as observableForkJoin} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {forkJoin, forkJoin as observableForkJoin} from 'rxjs';
import { of } from 'rxjs/internal/observable/of';
import { catchError, switchMap } from 'rxjs/operators';
import { LanguageService } from '../../services/language/language.service';
import {TermService} from '../../services/term/term.service';
import {Disease, Gene, LoincEntry, Term, TermTree} from '../../models/models';
import {Disease, Gene, Language, LoincEntry, Term, TermTree, Translation} from '../../models/models';
import {DialogService} from '../../../shared/dialog-excel-download/dialog.service';

import {OntologyService} from "../../services/ontology/ontology.service";

@Component({
selector: 'app-term',
Expand All @@ -19,10 +21,7 @@ export class TermComponent implements OnInit {
termTitle: string;
query: string;
paramId: string;
term: Term = {
'id': '', 'name': '', 'definition': '', 'altTermIds': [], 'comment': '', 'synonyms': [],
'isObsolete': true, 'xrefs': [], 'pubmedXrefs': [], 'purl': ''
};
term: Term;
geneColumns = ['geneId', 'dbDiseases'];
geneSource: MatTableDataSource<Gene>;
geneAssocCount: number;
Expand All @@ -48,13 +47,15 @@ export class TermComponent implements OnInit {
overlay = false;
displayAllDiseaseAssc = false;
displayAllGeneAssc = false;
languages: Language[];
selectedLanguage: Language = {language: "en", language_long: "English"}

@ViewChild('diseasePaginator', {static: true}) diseasePaginator: MatPaginator;
@ViewChild('genePaginator', {static: true}) genePaginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;

constructor(private route: ActivatedRoute, private termService: TermService, private dialogService: DialogService,
private router: Router) {
constructor(private route: ActivatedRoute, private termService: TermService, private ontologyService: OntologyService,
private dialogService: DialogService, private languageService: LanguageService, private router: Router) {
}

ngOnInit() {
Expand Down Expand Up @@ -100,26 +101,28 @@ export class TermComponent implements OnInit {
}

refreshData(query: string) {
this.termService.searchTerm(query)
.subscribe((data) => {
this.setDefaults(data.details);
const maxTermWidth = 100;
this.treeData = data.relations;
this.treeData.maxTermWidth = maxTermWidth;
const termCount = this.treeData.termCount;
this.treeData.children.sort((a, b) => a.childrenCount > b.childrenCount ? (-1) : 1);
this.treeData.children.map(term => {
const percent = term.childrenCount / termCount;
const newWidth = Math.ceil(maxTermWidth * percent);
const newMargin = -115 + ((maxTermWidth - newWidth) - 5);
term.treeCountWidth = newWidth;
term.treeMargin = newMargin;
});
this.termTitle = this.term.name;
}, (error) => {
// Error bubbles up
console.log(error);
forkJoin( {
hpo: this.termService.searchTerm(query),
term: this.ontologyService.term(query).pipe(catchError(e => { console.error(e); return of(undefined)})),
parents: this.ontologyService.parents(query).pipe(catchError(e => of([]))),
children: this.ontologyService.children(query).pipe(catchError(e => of([])))
}).subscribe(({hpo, term, parents, children}) => {
this.setDefaults(term);
const maxTermWidth = 100;
hpo.relations = this.joinTranslation(hpo.relations, parents, children);
this.treeData = hpo.relations;
this.treeData.maxTermWidth = maxTermWidth;
const termCount = this.treeData.termCount;
this.treeData.children.sort((a, b) => a.childrenCount > b.childrenCount ? (-1) : 1);
this.treeData.children.map(term => {
const percent = term.childrenCount / termCount;
const newWidth = Math.ceil(maxTermWidth * percent);
const newMargin = -115 + ((maxTermWidth - newWidth) - 5);
term.treeCountWidth = newWidth;
term.treeMargin = newMargin;
});
this.termTitle = this.term.name;
});
}

reloadDiseaseAssociations(offset: string, max: string) {
Expand Down Expand Up @@ -158,12 +161,48 @@ export class TermComponent implements OnInit {
this.term.definition = (term.definition != null) ? term.definition : 'Sorry this term has no definition.';
this.term.purl = 'http://purl.obolibrary.org/obo/' + term.id.replace(':', '_');
this.term.xrefs = (term.xrefs != null) ? term.xrefs : [];
this.term.pubmedXrefs = (term.pubmedXrefs != null) ? term.pubmedXrefs.map(pmid => {
return {whole: pmid, id: pmid.split(':')[1]};
}) : [];

if (term.translations != undefined && term.translations.length > 0){
// Get unique set of languages
this.languages = [...new Set(term.translations.map((t) => {
return {language: t.language, language_long: t.language_long}
}))];

// Add english default
this.languages.unshift(this.languageService.default);

// If the active language is updated set it.
this.languageService.active$.subscribe((active) => {
const exist = this.languages.some(li => li.language == active.language);
if (exist) {
this.selectedLanguage = active;
} else {
this.languageService.change(this.languageService.default);
}
});
}
}
}

joinTranslation(relations, parents, children){
relations.children.map((child) => {
children.map((childT) => {
if(child.ontologyId == childT.id){
child.translations = childT.translations
}
});
});

relations.parents.map((parent) => {
parents.map((parentT) => {
if(parent.ontologyId == parentT.id){
parent.translations = parentT.translations
}
});
});
return relations;
}

showAllDiseases(event) {
this.assocLoading = true;
this.reloadDiseaseAssociations('0', '-1');
Expand Down Expand Up @@ -217,6 +256,10 @@ export class TermComponent implements OnInit {
setTreeStyles(child: Term): any {
return {'width': child.treeCountWidth + 'px', 'margin-left': child.treeMargin + 'px', 'margin-right': '20px'};
}

changeLanguage(language){
this.languageService.change(language);
}
}


Expand Down
Loading

0 comments on commit 9d46eb0

Please sign in to comment.