diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1bce845dcaea8224e41ed2482721bc8b86a80e36..098e7f6fe33e72a87a111ee4c56d26a97621c492 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -6577,6 +6577,11 @@ "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "dev": true }, + "ng-mocks": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-7.6.0.tgz", + "integrity": "sha512-Zorpd5I6KmvTtiYwcjymzCaortznMZr5CRB737XaNheITTUb2rVLUoEBk1dwQE3b/Cp5sByuS85fzwJRvjEXKQ==" + }, "ngx-moment": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/ngx-moment/-/ngx-moment-3.3.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8d03d8c9108afda28c9d2b06d63fcb296b4f3901..ecf1d222fecc1eacf6ed1e1ff8967b339829f9c4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,15 +21,16 @@ "@angular/platform-browser": "7.2.7", "@angular/platform-browser-dynamic": "7.2.7", "@angular/router": "7.2.7", + "@ng-bootstrap/ng-bootstrap": "4.0.0", "@types/leaflet": "1.2.14", "@types/leaflet.markercluster": "1.0.3", - "@ng-bootstrap/ng-bootstrap": "4.0.0", "bootstrap": "4.1.3", "core-js": "2.5.7", "font-awesome": "4.7.0", - "moment": "2.24.0", "leaflet": "1.3.4", "leaflet.markercluster": "1.4.1", + "moment": "2.24.0", + "ng-mocks": "^7.6.0", "ngx-moment": "3.3.0", "popper.js": "1.14.6", "rxjs": "6.4.0", diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index ce2119956592d018f78cc0ae550b234dda1dc0d6..8a99e373a1ec2e6ba6f918f47c2dc62ff022cb28 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -23,6 +23,8 @@ import { CardSectionComponent } from './card-section/card-section.component'; import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.component'; import { CardTableComponent } from './card-table/card-table.component'; import { MomentModule } from 'ngx-moment'; +import { XrefsComponent } from './xrefs/xrefs.component'; + @NgModule({ declarations: [ @@ -43,6 +45,7 @@ import { MomentModule } from 'ngx-moment'; CardSectionComponent, LoadingSpinnerComponent, CardTableComponent, + XrefsComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.html b/frontend/src/app/germplasm-card/germplasm-card.component.html index 80c26df8635c7d730f6a0027a35f8f744ec29da1..7ffb091702f715389456a2878f6eaeb898c777fc 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.html +++ b/frontend/src/app/germplasm-card/germplasm-card.component.html @@ -660,4 +660,9 @@ </ng-container> </div> </div> + + + <!--XRefs part --> + <gpds-xrefs [xrefId]="germplasmGnpis.germplasmPUI"></gpds-xrefs> </ng-container> + diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts b/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts index b94e00d4acaf7cd9d48b4566166f2e1dadadbcd5..60c1d275dbd873840c66b307a687bf8975406fec 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts +++ b/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts @@ -19,6 +19,9 @@ import { Germplasm, GermplasmData, GermplasmResult, Institute, Origin, Site } fr import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; import { MomentModule } from 'ngx-moment'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; +import { MockComponent } from 'ng-mocks'; +import { XrefsComponent } from '../xrefs/xrefs.component'; + describe('GermplasmCardComponent', () => { @@ -74,7 +77,7 @@ describe('GermplasmCardComponent', () => { }; const brapiGermplasmPedigree: GermplasmResult<BrapiGermplasmPedigree> = { - result : { + result: { germplasmDbId: '12', defaultDisplayName: '12', pedigree: null, @@ -139,7 +142,7 @@ describe('GermplasmCardComponent', () => { const brapiGermplasmAttributes: GermplasmResult<GermplasmData<BrapiGermplasmAttributes[]>> = { result: { - data: [ { + data: [{ attributeName: 'longueur', value: '30' }] @@ -202,13 +205,14 @@ describe('GermplasmCardComponent', () => { TestBed.configureTestingModule({ imports: [RouterTestingModule, NgbPopoverModule, MomentModule], declarations: [ - GermplasmCardComponent, LoadingSpinnerComponent + GermplasmCardComponent, LoadingSpinnerComponent, MockComponent(XrefsComponent) ], providers: [ // { provide: ActivatedRoute, useValue: activatedRoute }, { provide: BrapiService, useValue: brapiService }, { provide: GnpisService, useValue: gnpisService }, - { provide: ActivatedRoute, + { + provide: ActivatedRoute, useValue: { snapshot: { queryParams: convertToParamMap({ diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index 6b78cfe969af2fe6d3ad53ddca004d9f23ab3393..1a7cee829791e7c9c2517227e27220e3142bc895 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -5,6 +5,7 @@ import { Germplasm } from './models/gnpis.germplasm.model'; import { DataDiscoveryCriteria, DataDiscoveryFacet, DataDiscoveryResults, DataDiscoverySource } from './models/data-discovery.model'; import { BrapiResults } from './models/brapi.model'; import { map } from 'rxjs/operators'; +import { XrefResponse } from './models/xref.model'; export const BASE_URL = 'gnpis/v1/datadiscovery'; @@ -106,4 +107,9 @@ export class GnpisService { getSource(sourceURI: string): Observable<DataDiscoverySource> { return this.sourceByURI$.pipe(map(sourceByURI => sourceByURI[sourceURI])); } + + xref(xrefId: string): Observable<XrefResponse> { + return this.http.get<XrefResponse>(`gnpis/v1/xref/documentbyfulltextid?linkedRessourcesID=${xrefId}`); + } + } diff --git a/frontend/src/app/models/xref.model.ts b/frontend/src/app/models/xref.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..b695b86603130728a31771e010b53d2fada1cfda --- /dev/null +++ b/frontend/src/app/models/xref.model.ts @@ -0,0 +1,11 @@ +export interface XrefModel { + + url: string; + description: string; + database_name: string; + entry_type: string; + db_version: string; + +} + +export type XrefResponse = XrefModel[]; diff --git a/frontend/src/app/site-card/site-card.component.html b/frontend/src/app/site-card/site-card.component.html index 2bfeb13c17d399647477f70da54016bc71e6a71e..9144f1aff1b091e95ac7394958aafdf6b09e138e 100644 --- a/frontend/src/app/site-card/site-card.component.html +++ b/frontend/src/app/site-card/site-card.component.html @@ -3,10 +3,14 @@ <ng-container *ngIf="location"> <h3> Site: {{ location.locationName }} + + </h3> <gpds-map [locations]="[location]" *ngIf="location && location.latitude && location.longitude"></gpds-map> + + <gpds-card-section header="Details"> <ng-template> @@ -75,4 +79,8 @@ </gpds-card-table> </ng-template> </gpds-card-section> + + <!--XRefs part --> + <gpds-xrefs [xrefId]="location.locationDbId"></gpds-xrefs> + </ng-container> diff --git a/frontend/src/app/site-card/site-card.component.spec.ts b/frontend/src/app/site-card/site-card.component.spec.ts index 322fb4b94eb7677e700782b6886e57effa33e007..f33d7f9a27f679d1748082027c638a7b7c58054a 100644 --- a/frontend/src/app/site-card/site-card.component.spec.ts +++ b/frontend/src/app/site-card/site-card.component.spec.ts @@ -10,6 +10,9 @@ import { CardRowComponent } from '../card-row/card-row.component'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; import { CardTableComponent } from '../card-table/card-table.component'; import { CardSectionComponent } from '../card-section/card-section.component'; +import { MockComponent } from 'ng-mocks'; +import { XrefsComponent } from '../xrefs/xrefs.component'; + describe('SiteCardComponent', () => { const brapiService = jasmine.createSpyObj( @@ -48,7 +51,8 @@ describe('SiteCardComponent', () => { TestBed.configureTestingModule({ declarations: [ SiteCardComponent, MapComponent, LoadingSpinnerComponent, - CardRowComponent, CardSectionComponent, CardTableComponent + CardRowComponent, CardSectionComponent, CardTableComponent, + MockComponent(XrefsComponent) ], providers: [ { provide: BrapiService, useValue: brapiService }, diff --git a/frontend/src/app/site-card/site-card.component.ts b/frontend/src/app/site-card/site-card.component.ts index c92353088b7450ebf51fc75f54ae7b16e870959f..d892c85d2bbba0d545584126912530f94174ddd4 100644 --- a/frontend/src/app/site-card/site-card.component.ts +++ b/frontend/src/app/site-card/site-card.component.ts @@ -15,6 +15,7 @@ export class SiteCardComponent implements OnInit { additionalInfos: KeyValueObject[]; loading = true; + constructor(private brapiService: BrapiService, private route: ActivatedRoute) { } @@ -22,11 +23,9 @@ export class SiteCardComponent implements OnInit { this.route.paramMap.subscribe(paramMap => { // initialize site from location ID const locationId = paramMap.get('id'); - this.brapiService.location(locationId).subscribe( response => { this.location = response.result; - this.additionalInfos = []; if (this.location.additionalInfo) { this.additionalInfos = KeyValueObject.fromObject(this.location.additionalInfo); diff --git a/frontend/src/app/study-card/study-card.component.html b/frontend/src/app/study-card/study-card.component.html index f12d1173df29c6a5aeb0706915d9fb0f4a210a6c..0ccf9c30277119280eb3752e06c2150aa5692c2c 100644 --- a/frontend/src/app/study-card/study-card.component.html +++ b/frontend/src/app/study-card/study-card.component.html @@ -254,5 +254,7 @@ </ng-template> </gpds-card-section> -</ng-container> + <!--XRefs part --> + <gpds-xrefs [xrefId]="study.studyDbId"></gpds-xrefs> +</ng-container> diff --git a/frontend/src/app/study-card/study-card.component.spec.ts b/frontend/src/app/study-card/study-card.component.spec.ts index e2407e4792107bf857cc42f5c40fa3c3720c924b..e9e0e8cf56bcaff0293df0b80e4a4383494164cd 100644 --- a/frontend/src/app/study-card/study-card.component.spec.ts +++ b/frontend/src/app/study-card/study-card.component.spec.ts @@ -23,6 +23,9 @@ import { CardSectionComponent } from '../card-section/card-section.component'; import { CardRowComponent } from '../card-row/card-row.component'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; import { CardTableComponent } from '../card-table/card-table.component'; +import { MockComponent } from 'ng-mocks'; +import { XrefsComponent } from '../xrefs/xrefs.component'; + describe('StudyCardComponent', () => { beforeEach(() => jasmine.addMatchers(speculoosMatchers)); @@ -204,7 +207,7 @@ describe('StudyCardComponent', () => { imports: [RouterTestingModule], declarations: [ StudyCardComponent, MapComponent, CardSectionComponent, - CardRowComponent, LoadingSpinnerComponent, CardTableComponent + CardRowComponent, LoadingSpinnerComponent, CardTableComponent, MockComponent(XrefsComponent) ], providers: [ { provide: ActivatedRoute, useValue: activatedRoute }, diff --git a/frontend/src/app/xrefs/xrefs.component.html b/frontend/src/app/xrefs/xrefs.component.html new file mode 100644 index 0000000000000000000000000000000000000000..36d80e9b53d2d7183765359c9bcb343028b2a4e5 --- /dev/null +++ b/frontend/src/app/xrefs/xrefs.component.html @@ -0,0 +1,22 @@ +<gpds-card-section *ngIf="xrefs.length > 0" + header="Cross References"> + <ng-template> + <gpds-card-table + [headers]="[ + 'Name', + 'Source', + 'Type', + 'Description' + ]" + [rows]=xrefs> + <ng-template let-crossRef> + <tr> + <td><a [href]="crossRef.url">{{ crossRef.db_version }}</a></td> + <td>{{ crossRef.database_name }}</td> + <td>{{ crossRef.entry_type }}</td> + <td>{{ crossRef.description | slice:0:120 }}...</td> + </tr> + </ng-template> + </gpds-card-table> + </ng-template> +</gpds-card-section> diff --git a/frontend/src/app/xrefs/xrefs.component.scss b/frontend/src/app/xrefs/xrefs.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/frontend/src/app/xrefs/xrefs.component.spec.ts b/frontend/src/app/xrefs/xrefs.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4ce15e8bed6be6c84f9946697065913fca87155 --- /dev/null +++ b/frontend/src/app/xrefs/xrefs.component.spec.ts @@ -0,0 +1,81 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { XrefsComponent } from './xrefs.component'; +import { ComponentTester, speculoosMatchers } from 'ngx-speculoos'; +import { XrefModel } from '../models/xref.model'; +import { of } from 'rxjs'; +import { GnpisService } from '../gnpis.service'; +import { CardSectionComponent } from '../card-section/card-section.component'; +import { CardTableComponent } from '../card-table/card-table.component'; + +describe('XrefsComponent', () => { + beforeEach(() => jasmine.addMatchers(speculoosMatchers)); + + class XrefsComponentTester extends ComponentTester<XrefsComponent> { + constructor() { + super(XrefsComponent); + } + + get cardHeader() { + return this.element('div.card-header'); + } + + get columns() { + return this.elements('td'); + } + } + + const gnpisService = jasmine.createSpyObj( + 'GnpisService', [ + 'xref' + ] + ); + + const xref: XrefModel[] = [{ + url: 'https://urgi.versailles.inra.fr/association/association/viewer.do#results/analysisIds=1808038', + description: 'Col-Fa-b*_MLM+Q+K is a GWASd anté paneCol-Fa-b*_MLM+Q+K is aGAS anlysis involving CC_Qualité' + + 'djs dsqdsq djsqpodsjqodsqdsqkpdqpdWOLOLOLOOOOOOOsqpkdsqkdsqkdsqdsdsqdsqdsqddsqffjùsodfusjùfsfsd', + database_name: 'GnpIS', + entry_type: 'GWAS analysis', + db_version: 'GWAS_ANALYSIS_1808038_1' + }]; + + + const xrefBlank: XrefModel[] = []; + + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + XrefsComponent, CardSectionComponent, CardTableComponent + ], + providers: [ + { provide: GnpisService, useValue: gnpisService }, + ] + }); + })); + + it('should fetch the xref information', async(() => { + gnpisService.xref.and.returnValue(of(xref)); + const tester = new XrefsComponentTester(); + + tester.detectChanges(); + + expect(tester.cardHeader).toContainText('Cross References'); + expect(tester.columns[0]).toContainText(xref[0].db_version); + expect(tester.columns[1]).toContainText(xref[0].database_name); + expect(tester.columns[2]).toContainText(xref[0].entry_type); + expect(tester.columns[3].textContent.length).toBeLessThanOrEqual(124); + + })); + + + it('should not display cross references', async(() => { + gnpisService.xref.and.returnValue(of(xrefBlank)); + const tester = new XrefsComponentTester(); + tester.detectChanges(); + + expect(tester.cardHeader).toBeFalsy(); + + })); +}); diff --git a/frontend/src/app/xrefs/xrefs.component.ts b/frontend/src/app/xrefs/xrefs.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a7ff5c75aae044cfa2d294ca8d5f2214e5c90eb --- /dev/null +++ b/frontend/src/app/xrefs/xrefs.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { GnpisService } from '../gnpis.service'; +import { XrefModel } from '../models/xref.model'; + +@Component({ + selector: 'gpds-xrefs', + templateUrl: './xrefs.component.html', + styleUrls: ['./xrefs.component.scss'] +}) +export class XrefsComponent implements OnInit { + + xrefs: Array<XrefModel> = new Array<XrefModel>(); + @Input() xrefId: string; + + constructor(private gnpisService: GnpisService) { + } + + ngOnInit() { + this.gnpisService.xref(this.xrefId).subscribe( + xrefs => { + this.xrefs = xrefs; + } + ); + + } +}