diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 3b87c03f896f584172004ea031b0703edb5d653b..72884e5f8dcdc90cd8520ba2071eeeab1762dec3 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -21,18 +21,14 @@ logging.level: gpds: elasticsearch-alias-template: - gnpis_{source}_{documentType}_5432_scratchy-group{groupId} + gnpis_{source}_{documentType}_5432_krusty-group{groupId} elasticsearch-xref-index-name: - urgi_xref + urgi_xref_dev cropOntology-repository-url: https://urgi.versailles.inra.fr/files/ephesis/trait-ontology/data_16.3/ontology-repository.json - # TODO: Remove this and store at document generation directly (in BrAPI `observationVariable.documentationURL` field) - cropOntology-portal-link: - https://urgi.versailles.inra.fr/ephesis/ephesis/ontologyportal.do#termIdentifier= - # Provides access to user group WS used to query private data ES aliases security-user-group-ws-url: security-user-group-ws-token: diff --git a/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/datasources.jsonld b/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/datasources.jsonld index 8a860fa2469617a406b5e26976e448459e04354a..6b319a0137746dff7d0327143ee2aadc52623a76 100644 --- a/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/datasources.jsonld +++ b/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/datasources.jsonld @@ -40,7 +40,8 @@ "@id": "https://urgi.versailles.inra.fr", "schema:identifier": "URGI", "schema:name": "URGI GnpIS", - "schema:url": "https://urgi.versailles.inra.fr" + "schema:url": "https://urgi.versailles.inra.fr/gnpis/", + "schema:image": "./logos/GnpIS.png" }, { "@type": "schema:DataCatalog", diff --git a/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/logos/GnpIS.png b/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/logos/GnpIS.png new file mode 100644 index 0000000000000000000000000000000000000000..1c682eaa80e2bb8de17bf9d545608a316dd89833 Binary files /dev/null and b/backend/src/main/resources/fr/inra/urgi/gpds/repository/file/logos/GnpIS.png differ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 098e7f6fe33e72a87a111ee4c56d26a97621c492..362fd3605dad3122e12b3c7a254afdf3b5fa9efe 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1189,6 +1189,16 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "angular-coordinates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/angular-coordinates/-/angular-coordinates-1.0.0.tgz", + "integrity": "sha1-IcuYpv+PTV6LWLjOjvzBP7elnug=" + }, + "angular-mocks": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.8.tgz", + "integrity": "sha512-LB13ESBT0eJrhQhfPXyLR9qm4LI9g44hyBFwUqZKEHEA4DpfxVTu0ONipiNoN0zWtmEAezA8u2gjcoaO2TStig==" + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -1258,6 +1268,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, + "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2458,7 +2469,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "constants-browserify": { "version": "1.0.0", @@ -2854,7 +2866,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "dev": true, + "optional": true }, "depd": { "version": "1.1.2", @@ -3821,7 +3834,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3842,12 +3856,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3862,17 +3878,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3989,7 +4008,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4001,6 +4021,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4015,6 +4036,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4022,12 +4044,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4046,6 +4070,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4126,7 +4151,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4138,6 +4164,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4223,7 +4250,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4259,6 +4287,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4278,6 +4307,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4321,12 +4351,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -4335,6 +4367,7 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -4347,6 +4380,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, + "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -4384,7 +4418,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "dev": true, + "optional": true }, "get-stream": { "version": "3.0.0", @@ -4564,7 +4599,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "dev": true, + "optional": true }, "has-value": { "version": "1.0.0", @@ -5284,7 +5320,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "dev": true, + "optional": true }, "is-windows": { "version": "1.0.2", @@ -5927,6 +5964,7 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -5939,7 +5977,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -6214,7 +6253,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "dev": true, + "optional": true }, "map-visit": { "version": "1.0.0", @@ -6862,6 +6902,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, + "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -7868,6 +7909,7 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, + "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -7879,6 +7921,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -7889,7 +7932,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -7898,6 +7942,7 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, + "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -7908,6 +7953,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -7918,6 +7964,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, + "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -9192,6 +9239,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -10548,6 +10596,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "optional": true, "requires": { "string-width": "^1.0.2 || 2" } diff --git a/frontend/package.json b/frontend/package.json index ecf1d222fecc1eacf6ed1e1ff8967b339829f9c4..20b0faab9a271c44b216f69db0b1f105dc16c904 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,8 @@ "@ng-bootstrap/ng-bootstrap": "4.0.0", "@types/leaflet": "1.2.14", "@types/leaflet.markercluster": "1.0.3", + "angular-coordinates": "1.0.0", + "angular-mocks": "1.7.8", "bootstrap": "4.1.3", "core-js": "2.5.7", "font-awesome": "4.7.0", diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index baefb2f81feef7dc3b34054582c20fd86b921c24..3833afd2b7e91c006e2a4ca16858050827862206 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -5,15 +5,15 @@ import { GermplasmCardComponent } from './germplasm-card/germplasm-card.componen import { StudyCardComponent } from './study-card/study-card.component'; import { SiteCardComponent } from './site-card/site-card.component'; -const routes: Routes = [ - { path: 'germplasm', component: GermplasmCardComponent }, +export const routes: Routes = [ { path: 'studies/:id', component: StudyCardComponent }, { path: 'sites/:id', component: SiteCardComponent }, { path: '', component: ResultPageComponent }, + { path: 'germplasm', component: GermplasmCardComponent } ]; @NgModule({ - imports: [RouterModule.forRoot(routes)], + imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })], exports: [RouterModule] }) export class AppRoutingModule { diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index feb91773f62d372bf293b41a03a4e1debf5076b5..1bdaa071a44e1fcc6bac0e097cdaccdbcd195b9d 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,4 +1,7 @@ <gpds-navbar></gpds-navbar> +<div class="alert alert-warning text-center mb-4" role="alert"> +This is a <b>beta</b> application. Please <a href="mailto:urgi-contact@inra.fr" class="alert-link" style="text-decoration: underline">contact us</a> for any comments +</div> <div class="container"> <gpds-error></gpds-error> <router-outlet></router-outlet> diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 8a99e373a1ec2e6ba6f918f47c2dc62ff022cb28..a6a37d9da503c9e7116a23e4dabb5f8e008e8092 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -24,6 +24,7 @@ import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.compo import { CardTableComponent } from './card-table/card-table.component'; import { MomentModule } from 'ngx-moment'; import { XrefsComponent } from './xrefs/xrefs.component'; +import { CoordinatesModule } from 'angular-coordinates'; @NgModule({ @@ -58,7 +59,8 @@ import { XrefsComponent } from './xrefs/xrefs.component'; NgbPopoverModule, FormsModule, ReactiveFormsModule, - MomentModule + MomentModule, + CoordinatesModule ], providers: [ { provide: HTTP_INTERCEPTORS, useExisting: ErrorInterceptorService, multi: true } diff --git a/frontend/src/app/brapi.service.spec.ts b/frontend/src/app/brapi.service.spec.ts index aecbcbbe4287507d1d5241348d4dd127d88c7a0f..a83f8ef65fad2c060aa12c80739ee6b109e59f07 100644 --- a/frontend/src/app/brapi.service.spec.ts +++ b/frontend/src/app/brapi.service.spec.ts @@ -2,26 +2,22 @@ import { BrapiService } from './brapi.service'; import { BrapiContacts, BrapiGermplasm, + BrapiGermplasmAttributes, + BrapiGermplasmPedigree, + BrapiGermplasmProgeny, BrapiLocation, BrapiObservationVariable, + BrapiProgeny, BrapiResult, BrapiResults, + BrapiSibling, BrapiStudy, BrapiTrial } from './models/brapi.model'; import { DataDiscoverySource } from './models/data-discovery.model'; -import { - BrapiDescriptor, - BrapiDonor, - BrapiGermplasmAttributes, - BrapiGermplasmPedigree, - BrapiGermplasmProgeny, - BrapiSet, - BrapiSibling -} from './models/brapi.germplasm.model'; -import { Germplasm, GermplasmData, GermplasmResult, Institute, Origin, Site } from './models/gnpis.germplasm.model'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; +import { Donor, Germplasm, GermplasmInstitute, GermplasmSet, Institute, Site } from './models/gnpis.model'; describe('BrapiService', () => { @@ -112,17 +108,49 @@ describe('BrapiService', () => { data: [{ germplasmDbId: 'g1', accessionNumber: 'G_10', + defaultDisplayName: null, + germplasmPUI: null, + pedigree: null, + seedSource: null, + synonyms: null, + commonCropName: null, + instituteCode: null, + instituteName: null, + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: null, + typeOfGermplasmStorageCode: null, + taxonIds: null, germplasmName: 'germplam1', genus: 'Populus', species: 'x generosa', - subtaxa: '' + subtaxa: '', + speciesAuthority: null, + subtaxaAuthority: null, + donors: null, + acquisitionDate: null }, { germplasmDbId: 'g2', accessionNumber: 'G_20', germplasmName: 'germplam2', genus: 'Triticum', species: 'aestivum', - subtaxa: 'subsp' + subtaxa: 'subsp', + defaultDisplayName: null, + germplasmPUI: null, + pedigree: null, + seedSource: null, + synonyms: null, + commonCropName: null, + instituteCode: null, + instituteName: null, + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: null, + typeOfGermplasmStorageCode: null, + taxonIds: null, + speciesAuthority: null, + subtaxaAuthority: null, + donors: null, + acquisitionDate: null }], } }; @@ -149,13 +177,14 @@ describe('BrapiService', () => { defaultDisplayName: 'frere1' }; - const brapiDescriptor: BrapiDescriptor = { - name: 'caracteristique1', - pui: '12', - value: '32' + const brapiProgeny: BrapiProgeny = { + germplasmDbId: 'test', + defaultDisplayName: 'progeny1', + parentType: 'parent1' }; - const brapiGermplasmPedigree: GermplasmResult<BrapiGermplasmPedigree> = { + const brapiGermplasmPedigree: BrapiResult<BrapiGermplasmPedigree> = { + metadata: null, result: { germplasmDbId: 'test', defaultDisplayName: '12', @@ -173,11 +202,12 @@ describe('BrapiService', () => { } }; - const brapiGermplasmProgeny: GermplasmResult<BrapiGermplasmProgeny> = { + const brapiGermplasmProgeny: BrapiResult<BrapiGermplasmProgeny> = { + metadata: null, result: { germplasmDbId: 'test', defaultDisplayName: '11', - progeny: [brapiSibling] + progeny: [brapiProgeny] } }; @@ -192,9 +222,9 @@ describe('BrapiService', () => { address: '12', logo: null }; - const origin: Origin = { + const origin: GermplasmInstitute = { + ...institute, institute: institute, - germplasmPUI: '12', accessionNumber: '12', accessionCreationDate: '1993', materialType: 'feuille', @@ -204,14 +234,15 @@ describe('BrapiService', () => { distributionStatus: null }; - const brapiDonor: BrapiDonor = { + const brapiDonor: Donor = { donorInstitute: institute, - germplasmPUI: '12', - accessionNumber: '12', - donorInstituteCode: 'urgi' + donorGermplasmPUI: '12', + donorAccessionNumber: '12', + donorInstituteCode: 'urgi', + donationDate: null }; - const brapiSet: BrapiSet = { + const germplasmSet: GermplasmSet = { germplasmCount: 12, germplasmRef: null, id: 12, @@ -219,18 +250,21 @@ describe('BrapiService', () => { type: 'plan' }; - const brapiGermplasmAttributes: GermplasmResult<GermplasmData<BrapiGermplasmAttributes[]>> = { + const brapiGermplasmAttributes: BrapiResult<BrapiGermplasmAttributes> = { + metadata: null, result: { + germplasmDbId: 'test', data: [{ + attributeCode: 'att', + attributeDbId: 'attr2', attributeName: 'longueur', + determinedDate: '2019', value: '30' }] } }; const germplasmTest: Germplasm = { - url: 'www.cirad.fr', - source: 'cirad', germplasmDbId: 'test', defaultDisplayName: 'test', accessionNumber: 'test', @@ -264,20 +298,15 @@ describe('BrapiService', () => { holdingGenbank: institute, presenceStatus: null, children: null, - descriptors: [brapiDescriptor], originSite: null, collectingSite: null, evaluationSites: null, collector: origin, breeder: origin, distributors: [origin], - panel: [brapiSet], - collection: [brapiSet], - population: [brapiSet] - }; - - const germplasmResultTest = { - result: germplasmTest + panel: [germplasmSet], + collection: [germplasmSet], + population: [germplasmSet] }; let brapiService: BrapiService; @@ -365,7 +394,7 @@ describe('BrapiService', () => { it('should fetch the pedigree', () => { - let fetchedGermplasmPedigree: GermplasmResult<BrapiGermplasmPedigree>; + let fetchedGermplasmPedigree: BrapiResult<BrapiGermplasmPedigree>; const germplasmDbId: string = brapiGermplasmPedigree.result.germplasmDbId; brapiService.germplasmPedigree(germplasmDbId).subscribe(response => { fetchedGermplasmPedigree = response; @@ -377,7 +406,8 @@ describe('BrapiService', () => { }); - it('should fetch the germplasm progeny', () => { + // TODO test the progeny call when the information about parent will be added + /*it('should fetch the germplasm progeny', () => { let fetchedGermplasmProgeny: GermplasmResult<BrapiGermplasmProgeny>; const germplasmDbId: string = brapiGermplasmProgeny.result.germplasmDbId; @@ -389,11 +419,11 @@ describe('BrapiService', () => { expect(fetchedGermplasmProgeny).toEqual(brapiGermplasmProgeny); - }); + });*/ it('should fetch the germplasm attributes', () => { - let fetchedGermplasmAttributes: GermplasmResult<GermplasmData<BrapiGermplasmAttributes[]>>; + let fetchedGermplasmAttributes: BrapiResult<BrapiGermplasmAttributes>; const germplasmDbId: string = germplasmTest.germplasmDbId; brapiService.germplasmAttributes(germplasmDbId).subscribe(response => { fetchedGermplasmAttributes = response; diff --git a/frontend/src/app/brapi.service.ts b/frontend/src/app/brapi.service.ts index bed9d9a83435a0485e9454ff4c1e317d51f0f891..a32154a558e86966cdc2a69014343a7c2649e6fc 100644 --- a/frontend/src/app/brapi.service.ts +++ b/frontend/src/app/brapi.service.ts @@ -1,9 +1,10 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; -import { Germplasm, GermplasmData, GermplasmResult } from './models/gnpis.germplasm.model'; import { BrapiGermplasm, + BrapiGermplasmAttributes, + BrapiGermplasmPedigree, BrapiLocation, BrapiObservationVariable, BrapiResult, @@ -11,7 +12,7 @@ import { BrapiStudy, BrapiTrial } from './models/brapi.model'; -import { BrapiGermplasmAttributes, BrapiGermplasmPedigree, BrapiGermplasmProgeny } from './models/brapi.germplasm.model'; +import { Germplasm } from './models/gnpis.model'; export const BASE_URL = 'brapi/v1'; @@ -28,18 +29,18 @@ export class BrapiService { .get<Germplasm>(`${BASE_URL}/germplasm/${germplasmDbId}`); } - germplasmPedigree(germplasmDbId: string): Observable<GermplasmResult<BrapiGermplasmPedigree>> { + germplasmPedigree(germplasmDbId: string): Observable<BrapiResult<BrapiGermplasmPedigree>> { return this.http - .get<GermplasmResult<BrapiGermplasmPedigree>>(`${BASE_URL}/germplasm/${germplasmDbId}/pedigree`); + .get<BrapiResult<BrapiGermplasmPedigree>>(`${BASE_URL}/germplasm/${germplasmDbId}/pedigree`); } - - germplasmProgeny(germplasmDbId: string): Observable<GermplasmResult<BrapiGermplasmProgeny>> { + // TODO use the progeny call when the information about parent will be added + /*germplasmProgeny(germplasmDbId: string): Observable<GermplasmResult<BrapiGermplasmProgeny>> { return this.http.get<GermplasmResult<BrapiGermplasmProgeny>>(`${BASE_URL}/germplasm/${germplasmDbId}/progeny`); - } + }*/ - germplasmAttributes(germplasmDbId: string): Observable<GermplasmResult<GermplasmData<BrapiGermplasmAttributes[]>>> { + germplasmAttributes(germplasmDbId: string): Observable<BrapiResult<BrapiGermplasmAttributes>> { return this.http - .get<GermplasmResult<GermplasmData<BrapiGermplasmAttributes[]>>>(`${BASE_URL}/germplasm/${germplasmDbId}/attributes`); + .get<BrapiResult<BrapiGermplasmAttributes>>(`${BASE_URL}/germplasm/${germplasmDbId}/attributes`); } study(studyDbId: string): Observable<BrapiResult<BrapiStudy>> { diff --git a/frontend/src/app/card-row/card-row.component.html b/frontend/src/app/card-row/card-row.component.html index e3d9f2587f5fe4f6857effbfb70828c6073eb35d..42301209673db46dd7a6c413ccd04b6bd375f749 100644 --- a/frontend/src/app/card-row/card-row.component.html +++ b/frontend/src/app/card-row/card-row.component.html @@ -1,4 +1,4 @@ -<ng-container *ngIf="test && (value === undefined || value)"> +<ng-container *ngIf="(test && value === undefined) || (value)"> <div class="row row-sep"> <div class="col-4 field my-2"> {{ label }} diff --git a/frontend/src/app/card-row/card-row.component.scss b/frontend/src/app/card-row/card-row.component.scss index 5f6ecd43d609ce9c88484a500fe32cc215501c3c..d5fab77cbcf99eb6409d67cec2b3906bb40d8186 100644 --- a/frontend/src/app/card-row/card-row.component.scss +++ b/frontend/src/app/card-row/card-row.component.scss @@ -1,7 +1,7 @@ -.row-sep { - border-top: 0.01px solid #f0f0f0; -} +@import '../../styles.scss'; .field { font-weight: bold; + overflow-wrap: normal; } + diff --git a/frontend/src/app/card-section/card-section.component.html b/frontend/src/app/card-section/card-section.component.html index ffaa0ba6b3a5afb787f18d12f65b042585dc4b8a..bcf4fd14571926df10dfd812722441942d0c646c 100644 --- a/frontend/src/app/card-section/card-section.component.html +++ b/frontend/src/app/card-section/card-section.component.html @@ -1,4 +1,4 @@ -<div class="card mb-3 mt-4" *ngIf="test"> +<div class="card mb-3" *ngIf="test"> <div class="card-header"> {{ header }} </div> diff --git a/frontend/src/app/card-section/card-section.component.scss b/frontend/src/app/card-section/card-section.component.scss index 155466cf1b6c0b21bbf28661089d1da24001ad64..78b6d191061a3269423b24f33facc74d9a1b2343 100644 --- a/frontend/src/app/card-section/card-section.component.scss +++ b/frontend/src/app/card-section/card-section.component.scss @@ -1,15 +1,17 @@ +@import '../../styles.scss'; + +a { + text-decoration: underline; +} + .card { - margin-top: 25px; - margin-bottom: 10px; - border-color: rgba(89, 89, 89, 0.33); - border-width: 1.5px; + border: none; } .card-header { font-size: large; font-weight: bold; - color: #0f6191; - background-color: #eaeaea; - border-color: rgba(29, 29, 29, 0.16); - border-width: 2.5px; + color: #f5f5f5; + background-image:repeating-linear-gradient(#0f96cd, #0f6191, #0f76a5) ; } + diff --git a/frontend/src/app/card-table/card-table.component.html b/frontend/src/app/card-table/card-table.component.html index beb8c09450a0d12acbc7dfdd9ff074745d034690..93b0d733aa34a09284d88cec0f4626d25ff022d0 100644 --- a/frontend/src/app/card-table/card-table.component.html +++ b/frontend/src/app/card-table/card-table.component.html @@ -1,4 +1,4 @@ -<div class="table-responsive scroll-table"> +<div class="table-responsive scroll-table table-card-body"> <table class="table table-sm table-striped"> <thead *ngIf="headers"> <tr> diff --git a/frontend/src/app/card-table/card-table.component.scss b/frontend/src/app/card-table/card-table.component.scss index f721891f8501e63874515eb0119c44fcf7ce090a..85f3be7e4aa2bc69e5f668af489d1a1c9f8bbc85 100644 --- a/frontend/src/app/card-table/card-table.component.scss +++ b/frontend/src/app/card-table/card-table.component.scss @@ -3,9 +3,17 @@ max-height: 200px; overflow-y: auto; - thead { + thead th { position: sticky; top: 0; background-color: white; } } + +.table-card-body { + padding: 0; + border: 2px solid rgb(195, 214, 226); + border-top-width: 0px; + border-bottom-left-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} diff --git a/frontend/src/app/form/form.component.html b/frontend/src/app/form/form.component.html index 3df3dbe20dcb972a632008200d563d2b1c63ada5..f0ffe68003cb3aa53f209e2d2dbaf9c583cfe4c2 100644 --- a/frontend/src/app/form/form.component.html +++ b/frontend/src/app/form/form.component.html @@ -10,7 +10,7 @@ <a tabindex="1" class="nav-link {{ activeTab == 'Variable' ? 'active' : ''}}" (click)="activeTab='Variable'"> - Variable + Trait </a> </li> </ul> diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.html b/frontend/src/app/germplasm-card/germplasm-card.component.html index 7ffb091702f715389456a2878f6eaeb898c777fc..7e3129e8ff3e41d6d0e74c53fb638fda16df5253 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.html +++ b/frontend/src/app/germplasm-card/germplasm-card.component.html @@ -1,668 +1,860 @@ <gpds-loading-spinner [loading]="loading" class="float-right"></gpds-loading-spinner> <ng-container *ngIf="germplasmGnpis"> - <h3> + <h3 class="mb-4"> + <img *ngIf="germplasmGnpis.holdingGenbank && germplasmGnpis.holdingGenbank.instituteName" + [src]="IMAGES_BRC_URL" + align="right"/> Germplasm: {{ germplasmGnpis.germplasmName }} </h3> - <div class="container-fluid"> - <div class="container"> - <div class="row"> - <div class="col-md-auto field" *ngIf="germplasmGnpis.photo && germplasmGnpis.photo.thumbnailFileName != null"> - <figure class="figure"> - <img - src="{{ IMAGES_SIREGAL_URL }}/{{ germplasmGnpis.holdingGenbank.instituteCode }}/{{ germplasmGnpis.photo.thumbnailFileName }}" - class="img-fluid"> - <figcaption class="figure-caption"> - <a class="btn popovers" data-boundary="window" placement="right" [ngbPopover]="imageTemplate" - [popoverTitle]="Details" container="body"> - Click to see more details. + <!-- Display the map --> + <gpds-map [locations]="germplasmLocations"></gpds-map> + + <div class="row align-items-center"> + + <!--Templates for gerplasm card--> + <ng-template #holdingInstituteTemplate> + + <gpds-card-row + label="" + [test]="germplasmGnpis.holdingInstitute && germplasmGnpis.holdingInstitute.logo"> + <ng-template> + <img + [src]="IMAGES_INSTITUTION_URL + germplasmGnpis.holdingInstitute.logo"/> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Code" + [value]="germplasmGnpis.holdingInstitute.instituteCode"> + </gpds-card-row> + + <gpds-card-row + label="Acronym" + [value]="germplasmGnpis.holdingInstitute.acronym"> + </gpds-card-row> + + <gpds-card-row + label="Organisation" + [value]="germplasmGnpis.holdingInstitute.organisation"> + </gpds-card-row> + + <gpds-card-row + label="Type" + [value]="germplasmGnpis.holdingInstitute.instituteType"> + </gpds-card-row> + + <gpds-card-row + label="Address" + [value]="germplasmGnpis.holdingInstitute.address"> + </gpds-card-row> + + <gpds-card-row + label="Website" + [test]="germplasmGnpis.holdingInstitute.webSite"> + <ng-template> + <a [href]="germplasmGnpis.breeder.institute.webSite" target="_blank"> + {{ germplasmGnpis.holdingInstitute.webSite }} + </a> + </ng-template> + </gpds-card-row> + + </ng-template> + + + <ng-template #BreederInstituteTemplate> + + <gpds-card-row + label="" + [test]="germplasmGnpis.breeder && germplasmGnpis.breeder.institute && germplasmGnpis.breeder.institute.logo"> + <ng-template> + <img + [src]="IMAGES_INSTITUTION_URL + germplasmGnpis.breeder.institute.logo"/> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Code" + [value]="germplasmGnpis.breeder.institute.instituteCode"> + </gpds-card-row> + + <gpds-card-row + label="Acronym" + [value]="germplasmGnpis.breeder.institute.acronym"> + </gpds-card-row> + + <gpds-card-row + label="Organisation" + [value]="germplasmGnpis.breeder.institute.organisation"> + </gpds-card-row> + + <gpds-card-row + label="Type" + [value]="germplasmGnpis.breeder.institute.instituteType"> + </gpds-card-row> + + <gpds-card-row + label="Address" + [value]="germplasmGnpis.breeder.institute.address"> + </gpds-card-row> + + <gpds-card-row + label="Website" + [test]="germplasmGnpis.breeder.institute.webSite"> + <ng-template> + <a [href]="germplasmGnpis.breeder.institute.webSite" target="_blank"> + {{ germplasmGnpis.breeder.institute.webSite }} + </a> + </ng-template> + </gpds-card-row> + + </ng-template> + + <ng-template #CollectorInstituteTemplate> + + <gpds-card-row + label="" + [test]="germplasmGnpis.collector && germplasmGnpis.collector.institute && germplasmGnpis.collector.institute.logo"> + <ng-template> + <img + [src]="IMAGES_INSTITUTION_URL + germplasmGnpis.collector.institute.logo"/> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Code" + [value]="germplasmGnpis.collector.institute.instituteCode"> + </gpds-card-row> + + <gpds-card-row + label="Acronym" + [value]="germplasmGnpis.collector.institute.acronym"> + </gpds-card-row> + + <gpds-card-row + label="Organisation" + [value]="germplasmGnpis.collector.institute.organisation"> + </gpds-card-row> + + <gpds-card-row + label="Address" + [value]="germplasmGnpis.collector.institute.address"> + </gpds-card-row> + + <gpds-card-row + label="Website" + [test]="germplasmGnpis.collector.institute.webSite"> + <ng-template> + <a [href]="germplasmGnpis.collector.institute.webSite" target="_blank"> + {{ germplasmGnpis.collector.institute.webSite }} + </a> + </ng-template> + </gpds-card-row> + + </ng-template> + + + <!--Section for the image representing the germplasm and the details about this image--> + <div class="col-auto field" *ngIf="germplasmGnpis.photo && germplasmGnpis.photo.thumbnailFileName"> + <a class="btn popovers" data-boundary="window" placement="right" [ngbPopover]="imageTemplate" + [popoverTitle]="germplasmGnpis.photo.photoName" container="body"> + <img + [src]="IMAGES_ACCESSION_URL + germplasmGnpis.holdingGenbank.instituteCode + '/' + germplasmGnpis.photo.thumbnailFileName" + class="img-fluid"> + <figcaption class="figure-caption"> + Click to see more details + </figcaption> + </a> + + <ng-template #imageTemplate> + <div class="card ngb-popover-window "> + <img class="card-img-top" + [src]="IMAGES_ACCESSION_URL + germplasmGnpis.holdingGenbank.instituteCode + '/' + germplasmGnpis.photo.fileName" + alt="" width="250px"> + <div class="card-body"> + + <gpds-card-row + label="Accession name" + [value]="germplasmGnpis.germplasmName"> + </gpds-card-row> + + <gpds-card-row + label="Photo name" + [value]="germplasmGnpis.photo.photoName"> + </gpds-card-row> + + <gpds-card-row + label="Description" + [value]="germplasmGnpis.photo.description"> + </gpds-card-row> + + <gpds-card-row + label="Copyright" + [value]="'© '+germplasmGnpis.photo.copyright"> + </gpds-card-row> + </div> + </div> + </ng-template> + </div> + + <!--Section for the information about the identification of the germplasm--> + <gpds-card-section + class="col-12 col-lg" + header="Identification"> + <ng-template> + <div class="card-body card-section-body"> + + <gpds-card-row + label="Germplasm name" + [value]="germplasmGnpis.germplasmName"> + </gpds-card-row> + + <gpds-card-row + label="Accession number" + [value]="germplasmGnpis.accessionNumber"> + </gpds-card-row> + + <gpds-card-row + label="Permanent Unique Identifier" + [value]="germplasmGnpis.germplasmPUI"> + </gpds-card-row> + + <gpds-card-row + label="Source" + [test]="germplasmSource"> + <ng-template> + <a target="_blank" + [href]="germplasmSource['schema:url']"> + <img [src]="germplasmSource['schema:image']" alt="Germplasm source image"/> + </a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Data source" + [test]="germplasmSource['schema:identifier'] && germplasmGnpis.documentationURL"> + <ng-template> + <a target="_blank" [href]="germplasmGnpis.documentationURL"> + Link to this study on {{ germplasmSource['schema:identifier'] }} </a> - </figcaption> - </figure> - - <ng-template #imageTemplate> - <div class="card ngb-popover-window "> - <img class="card-img-top" - src="{{ IMAGES_SIREGAL_URL }}/{{ germplasmGnpis.holdingGenbank.instituteCode }}/{{ germplasmGnpis.photo.fileName }}" - alt="" width="500px"> - <div class="card-body"> - <table class="table"> - <tr *ngIf="germplasmGnpis.photo.photoName"> - <th class="fieldName">Name</th> - <td class="field">{{ germplasmGnpis.photo.photoName }}</td> - </tr> - <tr *ngIf="germplasmGnpis.photo.description"> - <th class="fieldName">Description</th> - <td class="field">{{ germplasmGnpis.photo.description }}</td> - </tr> - <tr *ngIf="germplasmGnpis.photo.copyright"> - <th class="fieldName">Copyright</th> - <td class="field">{{ germplasmGnpis.photo.copyright }}</td> - </tr> - </table> + </ng-template> + </gpds-card-row> + + + <gpds-card-row + label="Accession synonyms" + [test]="germplasmGnpis.synonyms && germplasmGnpis.synonyms.length > 0"> + <ng-template> + <div class="content-overflow"> + {{ germplasmGnpis.synonyms.join(', ') }} </div> - </div> - </ng-template> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Taxon" + [test]="germplasmGnpis.genus || germplasmGnpis.species || germplasmGnpis.subtaxa"> + <ng-template> + <i>{{ germplasmGnpis.genus }} {{ germplasmGnpis.species }} {{ germplasmGnpis.subtaxa }}</i> + {{ germplasmGnpis.speciesAuthority ? '(' + germplasmGnpis.speciesAuthority + ')' : '' }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Taxon common names" + [test]="germplasmGnpis.taxonCommonNames && germplasmGnpis.taxonCommonNames.length > 0"> + <ng-template> + <div class="content-overflow"> + {{ germplasmGnpis.taxonCommonNames.join(', ') }} + </div> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Taxon synonyms" + [test]="germplasmGnpis.taxonSynonyms && germplasmGnpis.taxonSynonyms.length > 0"> + <ng-template> + <div class="content-overflow"> + <i>{{ germplasmGnpis.taxonSynonyms.join(', ') }}</i> + </div> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Biological status" + [value]="germplasmGnpis.biologicalStatusOfAccessionCode"> + </gpds-card-row> + + <gpds-card-row + label="Genetic nature" + [value]="germplasmGnpis.geneticNature"> + </gpds-card-row> + + <gpds-card-row + label="Seed source" + [value]="germplasmGnpis.seedSource"> + </gpds-card-row> + + <gpds-card-row + label="Pedigree" + [value]="germplasmGnpis.pedigree"> + </gpds-card-row> + + <gpds-card-row + label="Comments" + [value]="germplasmGnpis.comment"> + </gpds-card-row> </div> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Identification - </th> - </tr> - </thead> - <tr *ngIf="germplasmGnpis.accessionNumber"> - <th class="fieldName" scope="row">Accession number</th> - <td class="field">{{ germplasmGnpis.accessionNumber }}</td> - </tr> - <tr *ngIf="germplasmGnpis.acquisitionDate"> - <th class="fieldName" scope="row">Acquisition date</th> - <td - class="field">{{ germplasmGnpis.acquisitionDate | amParse:'YYYYMMDD' | amDateFormat:'YYYY-MM-DD' }}</td> - </tr> - <tr *ngIf="germplasmGnpis.germplasmName"> - <th class="fieldName" scope="row">Germplasm name</th> - <td class="field">{{ germplasmGnpis.germplasmName }}</td> - </tr> - <tr *ngIf="germplasmGnpis.germplasmPUI"> - <th class="fieldName" scope="row">Permanent Unique Identifier</th> - <td class="ellipsis field"> {{ germplasmGnpis.germplasmPUI }}</td> - </tr> - <tr *ngIf="germplasmGnpis.seedSource"> - <th class="fieldName" scope="row">Seed source</th> - <td class="field">{{ germplasmGnpis.seedSource }}</td> - </tr> - <tr *ngIf="germplasmGnpis.geneticNature"> - <th class="fieldName" scope="row">Genetic nature</th> - <td class="field">{{ germplasmGnpis.geneticNature }}</td> - </tr> - <tr *ngIf="germplasmGnpis.synonyms && germplasmGnpis.synonyms.length > 0"> - <th class="fieldName" scope="row">Accession synonyms</th> - <td class="field">{{ germplasmGnpis.synonyms.join(', ') }}</td> - </tr> - <tr> - <th class="fieldName" scope="row">Taxon</th> - <td class="field"> - <i>{{ germplasmGnpis.genus }} {{ germplasmGnpis.species }} {{ germplasmGnpis.subtaxa }}</i> - {{ germplasmGnpis.speciesAuthority ? '(' + germplasmGnpis.speciesAuthority + ')' : '' }} - </td> - </tr> - - <tr *ngIf="germplasmGnpis.taxonCommonNames && germplasmGnpis.taxonCommonNames.length > 0"> - <th class="fieldName" scope="row">Taxon common names</th> - <td class="ellipsis field"> {{ germplasmGnpis.taxonCommonNames.join(', ') }} - </td> - </tr> - <tr *ngIf="germplasmGnpis.taxonSynonyms && germplasmGnpis.taxonSynonyms.length > 0"> - <th class="fieldName" scope="row">Taxon synonyms</th> - <td class="scroll field"><i>{{ germplasmGnpis.taxonSynonyms.join(', ') }}</i></td> - </tr> - <tr *ngIf="germplasmGnpis.pedigree"> - <th class="fieldName" scope="row">Pedigree</th> - <td class="field">{{ germplasmGnpis.pedigree }}</td> - </tr> - <tr *ngIf="germplasmGnpis.biologicalStatusOfAccessionCode"> - <th class="fieldName" scope="row">Biological status</th> - <td class="field">{{ germplasmGnpis.biologicalStatusOfAccessionCode }}</td> - </tr> - <!--<tr> - <td>Source</td> - <td>{{ germplasmGnpis.source }}</td> - </tr> - <tr *ngIf="germplasmGnpis.source!='URGI'"> - <td>Source link</td> - <td><a>{{ germplasmGnpis.url }}</a></td> - </tr>--> - <tr *ngIf="germplasmGnpis.comment"> - <th class="fieldName" scope="row">Comments</th> - <td class="field">{{ germplasmGnpis.comment }}</td> - </tr> - </table> - </div> + </ng-template> + </gpds-card-section> + </div> + + <!--Section for the information about the holding of the germplasm--> + <gpds-card-section + header="Holding" + [test]="germplasmGnpis.holdingInstitute"> + <ng-template> + <div class="card-body card-section-body"> + + <gpds-card-row + label="Institution"> + <ng-template> + <a class="popover-underline" data-boundary="window" placement="top" + [ngbPopover]="holdingInstituteTemplate" + [popoverTitle]="germplasmGnpis.holdingInstitute.instituteName" + container="body"> + {{ germplasmGnpis.holdingInstitute.instituteName }}</a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Stock center name" + [test]="germplasmGnpis.holdingGenbank.instituteName && germplasmGnpis.holdingGenbank.webSite"> + <ng-template> + <a [href]="germplasmGnpis.holdingGenbank.webSite" target="_blank"> + {{ germplasmGnpis.holdingGenbank.instituteName }} + </a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Stock center name" + [test]="germplasmGnpis.holdingGenbank.instituteName && !germplasmGnpis.holdingGenbank.webSite"> + <ng-template> + {{ germplasmGnpis.holdingGenbank.instituteName }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Presence status" + [value]="germplasmGnpis.presenceStatus"> + </gpds-card-row> + </div> - </div> + </ng-template> + </gpds-card-section> - <div class="container"> - <div class="row" *ngIf="germplasmGnpis.holdingInstitute"> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Holding - </th> - </tr> - </thead> - <tr> - <th class="fieldName" scope="row">Institution</th> - <td class="ellipsis field"><a class="btn popovers" data-boundary="window" placement="top" - [ngbPopover]="holdingInstituteTemplate" - [popoverTitle]="germplasmGnpis.holdingInstitute.instituteName" - container="body"> - {{ germplasmGnpis.holdingInstitute.instituteName }}</a></td> - </tr> - <tr *ngIf="germplasmGnpis.holdingGenbank.instituteName"> - <th class="fieldName" scope="row">Stock center name</th> - <ng-container *ngIf="germplasmGnpis.holdingGenbank.webSite"> - <td class="field"> - <a href="germplasmGnpis.holdingGenbank.webSite">{{ germplasmGnpis.holdingGenbank.instituteName }}</a> - </td> - </ng-container> - <ng-container *ngIf="!germplasmGnpis.holdingGenbank.webSite"> - <td class="field">{{ germplasmGnpis.holdingGenbank.instituteName }}</td> - </ng-container> - </tr> - <tr *ngIf="germplasmGnpis.presenceStatus"> - <th class="fieldName" scope="row">Presence status</th> - <td class="field">{{ germplasmGnpis.presenceStatus }}</td> - </tr> - </table> - - - <ng-template #holdingInstituteTemplate> - <table class="popoverTable"> - <tr *ngIf="germplasmGnpis.holdingInstitute.instituteCode"> - <th class="fieldName" scope="row">FAO code</th> - <td class="field">{{ germplasmGnpis.holdingInstitute.instituteCode }}</td> - </tr> - <tr *ngIf="germplasmGnpis.holdingInstitute.instituteName"> - <th class="fieldName" scope="row">Institute name</th> - <td class="field">{{ germplasmGnpis.holdingInstitute.instituteName }}</td> - </tr> - <tr *ngIf="germplasmGnpis.holdingInstitute.acronym"> - <th class="fieldName" scope="row">Acronym</th> - <td class="field">{{ germplasmGnpis.holdingInstitute.acronym }}</td> - </tr> - <tr *ngIf="germplasmGnpis.holdingInstitute.organisation"> - <th class="fieldName" scope="row">Organisation</th> - <td class="field">{{ germplasmGnpis.holdingInstitute.organisation }}</td> - </tr> - <tr *ngIf="germplasmGnpis.holdingInstitute.instituteType"> - <th class="fieldName" scope="row">Institute type</th> - <td class="field">{{ germplasmGnpis.holdingInstitute.instituteType }}</td> - </tr> - <tr class="ellipsis" *ngIf="germplasmGnpis.holdingInstitute.webSite"> - <th class="fieldName" scope="row">Link</th> - <td class="ellipsis field"><a - href="{{ germplasmGnpis.holdingInstitute.webSite }}">{{ germplasmGnpis.holdingInstitute.webSite }}</a> - </td> - </tr> - <tr *ngIf="germplasmGnpis.holdingInstitute.address"> - <th class="fieldName" scope="row">Address</th> - <td class="field">{{ germplasmGnpis.holdingInstitute.address }}</td> - </tr> - </table> + <gpds-card-section + header="Collecting" + [test]="checkOriginCollecting()"> + <ng-template> + <div class="card-body card-section-body"> + + <gpds-card-row + label="Origin site" + [test]="germplasmGnpis.originSite && germplasmGnpis.originSite.siteName"> + <ng-template> + <a [routerLink]="['/sites/', germplasmGnpis.originSite.siteId]"> + {{ germplasmGnpis.originSite.siteName }} + </a> </ng-template> - </div> + </gpds-card-row> + + <gpds-card-row + label="Collecting site" + [test]="germplasmGnpis.collectingSite && germplasmGnpis.collectingSite.siteName"> + <ng-template> + <a [routerLink]="['/sites/', germplasmGnpis.collectingSite.siteId]"> + {{ germplasmGnpis.collectingSite.siteName }} + </a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Material type" + [test]="germplasmGnpis.collector && germplasmGnpis.collector.materialType"> + <ng-template> + {{ germplasmGnpis.collector.materialType }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Collectors" + [test]="germplasmGnpis.collector && germplasmGnpis.collector.collectors"> + <ng-template> + {{ germplasmGnpis.collector.collectors }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Acquisition/Creation date" + [test]="germplasmGnpis.collector.accessionCreationDate"> + <ng-template> + {{ germplasmGnpis.collector.accessionCreationDate | amParse:'YYYYMMDD' | amDateFormat:'YYYY-MM-DD' }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Acquisition/Creation date" + [test]="germplasmGnpis.acquisitionDate && !germplasmGnpis.collector.accessionCreationDate"> + <ng-template> + {{ germplasmGnpis.acquisitionDate }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Institution" + [test]="germplasmGnpis.collector.institute && germplasmGnpis.collector.institute.instituteName"> + <ng-template> + <a class="popovers" placement="top" + [ngbPopover]="CollectorInstituteTemplate" + [popoverTitle]="germplasmGnpis.collector.institute.instituteName"> + {{ germplasmGnpis.collector.institute.instituteName }} + </a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Accession number" + [test]="germplasmGnpis.collector && germplasmGnpis.collector.accessionNumber"> + <ng-template> + {{ germplasmGnpis.collector.accessionNumber }} + </ng-template> + </gpds-card-row> </div> + </ng-template> + </gpds-card-section> - <div class="row" - *ngIf="germplasmGnpis.breeder && germplasmGnpis.breeder.institute && germplasmGnpis.breeder.institute.instituteName"> - <div class="col"> - <table class="table table-sm"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Breeder - </th> - </tr> - </thead> - <tr> - <th class="fieldName" scope="row">Institution</th> - <td class="ellipsis field"> - <a class="btn popovers" placement="top" [ngbPopover]="BreederInstituteTemplate" - [popoverTitle]="germplasmGnpis.breeder.institute.instituteName"> - {{ germplasmGnpis.breeder.institute.instituteName }} {{ germplasmGnpis.breeder.institute.instituteCode }}</a> - </td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.accessionCreationDate"> - <th class="fieldName" scope="row">Accession Creation date</th> - <td - class="field">{{ germplasmGnpis.breeder.accessionCreationDate | amParse:'YYYYMMDD' | amDateFormat:'YYYY-MM-DD' }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.accessionNumber"> - <th class="fieldName" scope="row">Accession number</th> - <td class="field">{{ germplasmGnpis.breeder.accessionNumber }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.deregistrationYear"> - <th class="fieldName" scope="row">Deregistration year</th> - <td class="field">{{ germplasmGnpis.breeder.deregistrationYear }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.registrationYear"> - <th class="fieldName" scope="row">Registration year</th> - <td class="field">{{ germplasmGnpis.breeder.registrationYear }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.distributionStatus"> - <th class="fieldName" scope="row">distributionStatus</th> - <td class="field">{{ germplasmGnpis.breeder.distributionStatus }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.germplasmPUI"> - <th class="fieldName" scope="row">germplasmPUI</th> - <td class="field">{{ germplasmGnpis.breeder.germplasmPUI }}</td> - </tr> - - </table> - </div> + <gpds-card-section + header="Breeder" + [test]="checkBreeder()"> + <ng-template> + <div class="card-body card-section-body"> + + <gpds-card-row + label="Institute" + [test]="germplasmGnpis.breeder.institute && germplasmGnpis.breeder.institute.instituteName"> + <ng-template> + <a class="popovers" placement="top" + [ngbPopover]="BreederInstituteTemplate" + [popoverTitle]="germplasmGnpis.breeder.institute.instituteName"> + {{ germplasmGnpis.breeder.institute.instituteName }} + </a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Accession creation year" + [value]="germplasmGnpis.breeder.accessionCreationDate "> + </gpds-card-row> + + <gpds-card-row + label="Accession number" + [value]="germplasmGnpis.breeder.accessionNumber"> + </gpds-card-row> + + <gpds-card-row + label="Catalog registration year" + [value]="germplasmGnpis.breeder.registrationYear"> + </gpds-card-row> + + <gpds-card-row + label="Catalog deregistration year" + [value]="germplasmGnpis.breeder.deregistrationYear"> + </gpds-card-row> </div> + </ng-template> + </gpds-card-section> + + <gpds-card-section + header="Donor" + [test]="germplasmGnpis.donors && germplasmGnpis.donors.length > 0"> + <ng-template> + <gpds-card-table + [headers]="[ + 'Institute name', + 'Institute code', + 'Donation date', + 'Accession number', + 'Accession PUI' + ]" + [rows]="germplasmGnpis.donors"> + <ng-template let-row> + <tr> + <ng-template #InstituteTemplate> + + <gpds-card-row + label="" + [test]="row.donorInstitute && row.donorInstitute.logo"> + <ng-template> + <img + [src]="IMAGES_INSTITUTION_URL + row.donorInstitute.logo"/> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Code" + [value]="row.donorInstitute.instituteCode"> + </gpds-card-row> + + <gpds-card-row + label="Acronym" + [value]="row.donorInstitute.acronym"> + </gpds-card-row> + + <gpds-card-row + label="Organisation" + [value]="row.donorInstitute.organisation"> + </gpds-card-row> + + <gpds-card-row + label="Type" + [value]="row.donorInstitute.instituteType"> + </gpds-card-row> + + <gpds-card-row + label="Address" + [value]="row.donorInstitute.address"> + </gpds-card-row> + + <gpds-card-row + label="Website" + [test]="row.donorInstitute.webSite"> + <ng-template> + <a [href]=" row.donorInstitute.webSite " target="_blank"> + {{ row.donorInstitute.webSite }} + </a> + </ng-template> + </gpds-card-row> + </ng-template> + + <td> + <a class="popovers" placement="top" + [ngbPopover]="InstituteTemplate" + [popoverTitle]="row.donorInstitute.instituteName"> + {{ row.donorInstitute.instituteName }} + </a> + </td> + <td>{{ row.donorInstituteCode }}</td> + <td>{{ row.donationDate }}</td> + <td>{{ row.donorAccessionNumber }}</td> + <td>{{ row.donorGermplasmPUI }}</td> + </tr> + </ng-template> + + </gpds-card-table> + </ng-template> + </gpds-card-section> + + <gpds-card-section + header="Distributor" + [test]="germplasmGnpis.distributors && germplasmGnpis.distributors.length>0"> + <ng-template> + + <!--TODO : Add order column when ordering URL will be available--> + <gpds-card-table + [headers]="[ + 'Institute', + 'Accession number', + 'Distribution status' + + ]" + [rows]="germplasmGnpis.distributors"> + <ng-template let-row> + <tr> + <ng-template #InstituteTemplate> + + <gpds-card-row + label="" + [test]="row.institute && row.institute.logo"> + <ng-template> + <img + [src]="IMAGES_INSTITUTION_URL + row.institute.logo"/> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Code" + [value]="row.institute.instituteCode"> + </gpds-card-row> + + <gpds-card-row + label="Acronym" + [value]="row.institute.acronym"> + </gpds-card-row> + + <gpds-card-row + label="Organisation" + [value]="row.institute.organisation"> + </gpds-card-row> + + <gpds-card-row + label="Type" + [value]="row.institute.instituteType"> + </gpds-card-row> + + <gpds-card-row + label="Address" + [value]="row.institute.address"> + </gpds-card-row> + + <gpds-card-row + label="Website" + [test]="row.institute.webSite"> + <ng-template> + <a [href]="row.institute.webSite" target="_blank"> + {{ row.institute.webSite }} + </a> + </ng-template> + </gpds-card-row> + </ng-template> + + <td> + <a class="popovers" placement="top" + [ngbPopover]="InstituteTemplate" + [popoverTitle]="row.institute.instituteName"> + {{ row.institute.instituteName }} + </a> + </td> + <td>{{ row.accessionNumber }}</td> + <td>{{ row.distributionStatus }}</td> + </tr> + </ng-template> + + </gpds-card-table> + + </ng-template> + </gpds-card-section> + + <gpds-card-section + header="Evaluation Data" + [test]="germplasmAttributes && germplasmAttributes.length > 0"> + <ng-template> + <div class="card-body card-section-body"> + + <ng-container *ngFor="let descriptor of germplasmAttributes"> + <gpds-card-row + [label]="descriptor.attributeName" + [value]="descriptor.value"> + </gpds-card-row> + </ng-container> - <div class="row" *ngIf="germplasmGnpis.collector || germplasmGnpis.collectingSite"> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Collecting - </th> - </tr> - </thead> - <tr - *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.institute && germplasmGnpis.collector.institute.instituteName"> - <th class="fieldName" scope="row">Institution</th> - <td class="ellipsis field"><a class="btn popovers" placement="top" - [ngbPopover]="CollectorInstituteTemplate" - [popoverTitle]="germplasmGnpis.collector.institute.instituteName"> - {{ germplasmGnpis.collector.institute.instituteName }}</a></td> - </tr> - <tr *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.accessionCreationDate"> - <th class="fieldName" scope="row">Accession Creation date</th> - <td - class="field">{{ germplasmGnpis.collector.accessionCreationDate | amParse:'YYYYMMDD' | amDateFormat:'YYYY-MM-DD' }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.accessionNumber"> - <th class="fieldName" scope="row">Accession number</th> - <td class="field">{{ germplasmGnpis.collector.accessionNumber }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.collectors"> - <th class="fieldName" scope="row">collectors</th> - <td class="field">{{ germplasmGnpis.collector.collectors }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.distributionStatus"> - <th class="fieldName" scope="row">distributionStatus</th> - <td class="field">{{ germplasmGnpis.collector.distributionStatus }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.germplasmPUI"> - <th class="fieldName" scope="row">germplasmPUI</th> - <td class="field">{{ germplasmGnpis.collector.germplasmPUI }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector && germplasmGnpis.collector.materialType"> - <th class="fieldName" scope="row">Material type</th> - <td class="field">{{ germplasmGnpis.collector.materialType }}</td> - </tr> - - <tr *ngIf="germplasmGnpis.collectingSite && germplasmGnpis.collectingSite.siteName"> - <th class="fieldName" scope="row">Collecting site</th> - <td class="field"><a - [routerLink]="['/sites/', germplasmGnpis.collectingSite.siteId]">{{ germplasmGnpis.collectingSite.siteName }}</a> - </td> - </tr> - </table> - </div> </div> + </ng-template> + </gpds-card-section> + + <gpds-card-section + header="Genealogy" + [test]="checkPedigree() || checkProgeny()"> + <ng-template> + <div class="card-body card-section-body"> + + <gpds-card-row + label="Crossing plan" + [test]="germplasmPedigree && germplasmPedigree.crossingPlan"> + <ng-template> + {{ germplasmPedigree.crossingPlan }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Crossing year" + [test]="germplasmPedigree && germplasmPedigree.crossingYear"> + <ng-template> + {{ germplasmPedigree.crossingYear }} + </ng-template> + </gpds-card-row> - <div class="row" *ngIf="germplasmGnpis.donors && germplasmGnpis.donors.length > 0"> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Donation - </th> - </tr> - </thead> - <tr> - <th class="thead-light fieldName" scope="col">Institute</th> - <th class="thead-light fieldName" scope="col">Date</th> - </tr> - <tr *ngFor="let donor of germplasmGnpis.donors"> - <td class="ellipsis field"><a class="btn popovers ellipsis" placement="top" - [ngbPopover]="DonorInstituteTemplate" - [popoverTitle]="donor.donorInstitute.instituteName"> - {{ donor.donorInstitute.instituteName }}</a></td> - <td class="field" - *ngIf="donor.donationDate">{{ donor.donationDate | amParse:'YYYYMMDD' | amDateFormat:'YYYY-MM-DD' }}</td> - - <ng-template #DonorInstituteTemplate> - <table> - <tr *ngIf="donor.donorInstitute.instituteCode"> - <th class="fieldName" scope="row">Code</th> - <td class="field">{{ donor.donorInstitute.instituteCode }}</td> - </tr> - <tr *ngIf="donor.donorInstitute.acronym"> - <th class="fieldName" scope="row">Acronym</th> - <td class="field">{{ donor.donorInstitute.acronym }}</td> - </tr> - <tr *ngIf="donor.donorInstitute.organisation"> - <th class="fieldName" scope="row">Organisation</th> - <td class="field">{{ donor.donorInstitute.organisation }}</td> - </tr> - <tr *ngIf="donor.donorInstitute.instituteType"> - <th class="fieldName" scope="row">Type</th> - <td class="field">{{ donor.donorInstitute.instituteType }}</td> - </tr> - <tr *ngIf="donor.donorInstitute.webSite"> - <th class="fieldName" scope="row">Link</th> - <td class="ellipsis field"><a - href="{{ donor.donorInstitute.webSite }}">{{ donor.donorInstitute.webSite }}</a></td> - </tr> - <tr *ngIf="donor.donorInstitute.address"> - <th class="fieldName" scope="row">Address</th> - <td class="field">{{ donor.donorInstitute.address }}</td> - </tr> - </table> + <gpds-card-row + label="Family code" + [test]="germplasmPedigree && germplasmPedigree.familyCode"> + <ng-template> + {{ germplasmPedigree.familyCode }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Parent accessions" + [test]="germplasmPedigree && germplasmPedigree.parent1Name"> + + <ng-template> + <gpds-card-row + [label]="germplasmPedigree.parent1Type" + [test]="germplasmPedigree && germplasmPedigree.parent1DbId"> + <ng-template> + <a [routerLink]="['/germplasm']" + [queryParams]="{id:germplasmPedigree.parent1DbId}"> + {{ germplasmPedigree.parent1Name }} + </a> </ng-template> - </tr> - </table> - </div> - </div> + </gpds-card-row> - <div class="row"> - <div class="col"> - <ng-container *ngIf="germplasmGnpis.distributors && germplasmGnpis.distributors.length > 0"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Distribution - </th> - </tr> - </thead> - <ng-container *ngFor="let distributor of germplasmGnpis.distributors"> - <tr> - <th class="fieldName" scope="row">{{ distributor.institute.instituteName }}</th> - <td class="field">{{ distributor.distributionStatus }}</td> - </tr> + <gpds-card-row + [label]="germplasmPedigree.parent2Type" + [test]="germplasmPedigree && germplasmPedigree.parent2DbId"> + <ng-template> + <a [routerLink]="['/germplasm']" + [queryParams]="{id:germplasmPedigree.parent2DbId}"> + {{ germplasmPedigree.parent2Name }} + </a> + </ng-template> + </gpds-card-row> + + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Sibling accessions" + [test]="germplasmPedigree && (germplasmPedigree.siblings && germplasmPedigree.siblings.length > 0)"> + <ng-template> + + <div class="content-overflow"> + <ng-container *ngFor="let sibling of germplasmPedigree.siblings"> + <a [routerLink]="['/germplasm']" [queryParams]="{id:sibling.germplasmDbId }"> + {{ sibling.defaultDisplayName }} + </a>, </ng-container> - </table> + </div> + + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Descendant :" + [test]="checkProgeny()"> + </gpds-card-row> + + <div class="content-overflow-big"> + <ng-container *ngFor="let child of germplasmProgeny"> + + <gpds-card-row class="text" + [label]="child.secondParentName ? 'child(ren) of ' + child.firstParentName + ' x ' + child.secondParentName : 'child(ren) of ' + child.firstParentName" + [test]="checkProgeny()"> + <ng-template> + + <ng-container *ngFor="let sibling of child.sibblings"> + <a [routerLink]="['/germplasm']" [queryParams]="{id:sibling.pui}"> + {{ sibling.name }} + </a>, + </ng-container> + + </ng-template> + </gpds-card-row> </ng-container> </div> + </div> + </ng-template> + </gpds-card-section> - <ng-template #BreederInstituteTemplate> - <table> - <tr *ngIf="germplasmGnpis.breeder.institute.instituteCode"> - <th class="fieldName" scope="row">Code</th> - <td class="field">{{ germplasmGnpis.breeder.institute.instituteCode }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.institute.organisation"> - <th class="fieldName" scope="row">Organisation</th> - <td class="field">{{ germplasmGnpis.breeder.institute.organisation }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.institute.acronym"> - <th class="fieldName" scope="row">Acronym</th> - <td class="field">{{ germplasmGnpis.breeder.institute.acronym }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.institute.instituteType"> - <th class="fieldName" scope="row">Type</th> - <td class="field">{{ germplasmGnpis.breeder.institute.instituteType }}</td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.institute.webSite"> - <th class="fieldName" scope="row">Link</th> - <td class="ellipsis field"><a - href="{{ germplasmGnpis.breeder.institute.webSite }}">{{ germplasmGnpis.breeder.institute.webSite }}</a> - </td> - </tr> - <tr *ngIf="germplasmGnpis.breeder.institute.address"> - <th class="fieldName" scope="row">Address</th> - <td class="field">{{ germplasmGnpis.breeder.institute.address }}</td> - </tr> - </table> - </ng-template> + <gpds-card-section + header="Population" + [test]="germplasmGnpis.population && germplasmGnpis.population.length > 0"> + <ng-template> + <div class="card-body card-section-body card-section-body"> - <ng-template #CollectorInstituteTemplate> - <table> - <tr *ngIf="germplasmGnpis.collector.instituteCode"> - <th class="fieldName" scope="row">FAO code</th> - <td class="field">{{ germplasmGnpis.collector.instituteCode }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector.instituteName"> - <th class="fieldName" scope="row">Institute name</th> - <td class="field">{{ germplasmGnpis.collector.instituteName }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector.acronym"> - <th class="fieldName" scope="row">Acronym</th> - <td class="field">{{ germplasmGnpis.collector.acronym }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector.organisation"> - <th class="fieldName" scope="row">Organisation</th> - <td class="field">{{ germplasmGnpis.collector.organisation }}</td> - </tr> - <tr *ngIf="germplasmGnpis.collector.institute.webSite"> - <th class="fieldName" scope="row">Link</th> - <td class="ellipsis field"><a - href="{{ germplasmGnpis.collector.institute.webSite }}">{{ germplasmGnpis.collector.institute.webSite }}</a> - </td> - </tr> - <tr *ngIf="germplasmGnpis.collector.institute.address"> - <th class="fieldName" scope="row">Address</th> - <td class="field">{{ germplasmGnpis.collector.institute.address }}</td> - </tr> - </table> - </ng-template> + <ng-container *ngFor="let population of germplasmGnpis.population"> + <gpds-card-row + [label]="population.type ? population.name + ' (' + population.type + ')' : population.name" + [test]="population.germplasmRef && population.germplasmRef.pui && population.germplasmRef.pui != germplasmGnpis.germplasmDbId"> + <ng-template> + <a [routerLink]="['/germplasm']" + [queryParams]="{id:population.germplasmRef.pui}"> + {{ population.germplasmRef.name }} + </a> is composed by <a + [routerLink]="['/']" + [queryParams]="{germplasmLists: population.name, types: 'Germplasm'}"> + {{ population.germplasmCount }} accessions + </a> + </ng-template> + </gpds-card-row> + <gpds-card-row + [label]="population.type ? population.name + ' (' + population.type + ')' : population.name" + [test]="population.germplasmRef && population.germplasmRef.pui && population.germplasmRef.pui == germplasmGnpis.germplasmDbId"> + <ng-template> + {{ population.germplasmRef.name }} is composed by <a + [routerLink]="['/']" + [queryParams]="{germplasmLists: population.name, types: 'Germplasm'}"> + {{ population.germplasmCount }} accessions + </a> + </ng-template> + </gpds-card-row> + <gpds-card-row + [label]="population.type ? population.name + ' (' + population.type + ')' : population.name" + [test]="population.germplasmRef && !population.germplasmRef.pui"> + <ng-template> + <a [routerLink]="['/']" + [queryParams]="{germplasmLists: population.name, types: 'Germplasm'}"> + {{ population.germplasmCount }} accessions + </a> + </ng-template> + </gpds-card-row> + </ng-container> - <ng-container *ngIf="testPedigree() || testProgeny()"> - <div class="row"> - <div class="col"> - <h4>Genealogy</h4> - </div> - </div> - <div class="row"> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}" *ngIf="testPedigree()"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Ascendants - </th> - </tr> - </thead> - <tr *ngIf="germplasmPedigree.result.crossingPlan"> - <th class="fieldName" scope="row">Crossing plan</th> - <td class="field">{{ germplasmPedigree.result.crossingPlan }}</td> - </tr> - <tr *ngIf="germplasmPedigree.result.crossingYear"> - <th class="fieldName" scope="row">Crossing year</th> - <td class="field">{{ germplasmPedigree.result.crossingYear }}</td> - </tr> - <tr *ngIf="germplasmPedigree.result.familyCode"> - <th class="fieldName" scope="row">Family code</th> - <td class="field">{{ germplasmPedigree.result.familyCode }}</td> - </tr> - <tr *ngIf="(germplasmPedigree.result.parent1Type || germplasmPedigree.result.parent2Type) - && (germplasmPedigree.result.parent1Type !='UNDEFINED' || germplasmPedigree.result.parent2Type !='UNDEFINED')"> - <th scope="row">Parent accessions</th> - <td> - <table> - <ng-container [ngSwitch]="germplasmPedigree.result.parent1Type"> - <tr *ngSwitchCase="'FEMALE'"> - <th class="fieldName" scope="row">Mother</th> - <td class="field"><a - [routerLink]="'/germplasm'" - [queryParams]="{id:germplasmPedigree.result.parent1DbId}">{{ germplasmPedigree.result.parent1Name }}</a> - </td> - </tr> - <tr *ngSwitchCase="'MALE'"> - <th class="fieldName" scope="row">Father</th> - <td class="field"><a - [routerLink]="'/germplasm'" - [queryParams]="{id:germplasmPedigree.result.parent1DbId}">{{ germplasmPedigree.result.parent1Name }}</a> - </td> - </tr> - <tr *ngSwitchCase="'SELF'"> - <td>Self</td> - </tr> - <tr *ngSwitchCase="'POPULATION'"> - <th class="fieldName" scope="row">Population</th> - <td class="field">{{ germplasmPedigree.result.parent1Name }}</td> - </tr> - </ng-container> - <ng-container [ngSwitch]="germplasmPedigree.result.parent2Type"> - <tr *ngSwitchCase="'FEMALE'"> - <th class="fieldName" scope="row">Mother</th> - <td class="field"><a - [routerLink]="'/germplasm'" - [queryParams]="{id:germplasmPedigree.result.parent2DbId}">{{ germplasmPedigree.result.parent2Name }}</a></td> - </tr> - <tr *ngSwitchCase="'MALE'"> - <th class="fieldName" scope="row">Father</th> - <td class="field"><a - [routerLink]="'/germplasm'" - [queryParams]="{id:germplasmPedigree.result.parent2DbId}">{{ germplasmPedigree.result.parent2Name }}</a></td> - </tr> - <tr *ngSwitchCase="'SELF'"> - <td>Self</td> - </tr> - <tr *ngSwitchCase="'POPULATION'"> - <th class="fieldName" scope="row">Population</th> - <td class="field">{{ germplasmPedigree.result.parent2Name }}</td> - </tr> - </ng-container> - </table> - </td> - </tr> - </table> - </div> - </div> + </div> + </ng-template> + </gpds-card-section> - <ng-container *ngIf="germplasmPedigree.result.siblings && germplasmPedigree.result.siblings.length > 0"> - <div class="row "> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Siblings - </th> - </tr> - </thead> - <tbody> - <tr> - <th class="fieldName">Accession numbers</th> - <td class="scroll field"> - <ng-container *ngFor="let sibling of germplasmPedigree.result.siblings"> - <a routerLink="/germplasm" [queryParams]="{id:sibling.germplasmDbId }"> - {{ sibling.defaultDisplayName }}</a> - </ng-container> - </td> - </tr> - </tbody> - </table> - </div> - </div> + <gpds-card-section + header="Collection" + [test]="germplasmGnpis.collection && germplasmGnpis.collection.length > 0"> + <ng-template> + <div class="card-body card-section-body"> + + <ng-container *ngFor="let collection of germplasmGnpis.collection"> + <gpds-card-row + [label]="collection.type ? collection.name + ' (' + collection.type + ')' : collection.name"> + <ng-template> + <a [routerLink]="['/']" + [queryParams]="{germplasmLists: collection.name, types: 'Germplasm'}"> + {{ collection.germplasmCount }} accessions + </a> + </ng-template> + </gpds-card-row> </ng-container> - <ng-container *ngIf="testProgeny()"> - <div class="row "> - <div class="col"> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <thead class="text-white"> - <tr> - <th class="headerTitle" scope="col" colspan="2"> - Descendants - </th> - </tr> - </thead> - <tbody> - <tr> - <th class="fieldName">Accession numbers</th> - <td class="scroll field"> - <ng-container *ngFor="let child of germplasmProgeny.result.progeny"> - <a routerLink="/germplasm" [queryParams]="{id:child.germplasmDbId }"> - {{ child.defaultDisplayName }}</a> - </ng-container> - </td> - </tr> - </tbody> - </table> - </div> - </div> + </div> + </ng-template> + </gpds-card-section> + + + <gpds-card-section + header="Panel" + [test]="germplasmGnpis.panel && germplasmGnpis.panel.length > 0"> + <ng-template> + <div class="card-body card-section-body"> + + <ng-container *ngFor="let panel of germplasmGnpis.panel"> + <gpds-card-row + [label]="panel.type ? panel.name + ' (' + panel.type + ')' : panel.name"> + <ng-template> + <a [routerLink]="['/']" + [queryParams]="{germplasmLists: panel.name, types: 'Germplasm'}"> + {{ panel.germplasmCount }} accessions + </a> + </ng-template> + </gpds-card-row> </ng-container> - </ng-container> - - <ng-container *ngIf="germplasmAttributes && germplasmAttributes.length > 0"> - <div class="row"> - <div class="col"> - <h4 class="headerTitle">Evaluation Data</h4> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - - <ng-container *ngFor="let descriptor of germplasmAttributes"> - <tr> - <th class="fieldName" scope="row">{{ descriptor.attributeName }}</th> - <td class="field">{{ descriptor.value }}</td> - </tr> - </ng-container> - </table> - </div> - </div> - </ng-container> - - <ng-container *ngIf="germplasmGnpis.collection && germplasmGnpis.collection.length > 0"> - <div class="row"> - <div class="col"> - <h4 class="headerTitle">Collection</h4> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <ng-container *ngFor="let collection of germplasmGnpis.collection"> - <tr> - <th class="fieldName" scope="row"> - {{ collection.type ? collection.name + ' (' + collection.type + ')' : collection.name }} - </th> - <td class="ellipsis field"><a routerLink="" [queryParams]="{germplasmLists: collection.name, types: 'Germplasm'}"> - {{ collection.germplasmCount }} accessions</a> - </td> - </tr> - </ng-container> - </table> - </div> - </div> - </ng-container> - - <ng-container *ngIf="germplasmGnpis.panel && germplasmGnpis.panel.length > 0"> - <div class="row"> - <div class="col"> - <h4 class="headerTitle">Panel</h4> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <ng-container *ngFor="let panel of germplasmGnpis.panel"> - <tr> - <th class="fieldName" scope="row"> - {{ panel.type ? panel.name + ' (' + panel.type + ')' : panel.name }} - </th> - <td class="field"><a routerLink="" [queryParams]="{germplasmLists: panel.name, types: 'Germplasm'}"> - {{ panel.germplasmCount }} accessions</a></td> - </tr> - </ng-container> - </table> - </div> - </div> - </ng-container> - - <ng-container *ngIf="germplasmGnpis.population && germplasmGnpis.population.length > 0"> - <div class="row"> - <div class="col"> - <h4 class="headerTitle">Population</h4> - <table class="table table-sm .table-responsive{-sm|-md|-lg|-xl}"> - <ng-container *ngFor="let population of germplasmGnpis.population"> - <tr> - <th class="fieldName" scope="row"> - {{ population.type ? population.name + ' (' + population.type + ')' : population.name }} - </th> - <td class="field"><a routerLink="" [queryParams]="{germplasmLists: population.name, types: 'Germplasm'}"> - {{ population.germplasmCount }} accessions</a></td> - </tr> - </ng-container> - </table> - </div> - </div> - </ng-container> - </div> - </div> + </div> + </ng-template> + </gpds-card-section> <!--XRefs part --> - <gpds-xrefs [xrefId]="germplasmGnpis.germplasmPUI"></gpds-xrefs> + <gpds-xrefs [xrefId]="germplasmGnpis.germplasmPUI"></gpds-xrefs> </ng-container> diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.scss b/frontend/src/app/germplasm-card/germplasm-card.component.scss index 017b0c22841a99886f56cfab64b1ae23cfb1d1d0..f0376501baaeced5c8d3194b0d2d23faff4269fe 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.scss +++ b/frontend/src/app/germplasm-card/germplasm-card.component.scss @@ -1,3 +1,11 @@ @import "theme"; @import '../../styles.scss'; +a:not([href]):not([tabindex]) { + color: #0f6fa1; + cursor: pointer; +} + +a:not([href]):not([tabindex]):hover { + text-decoration: underline; +} 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 60c1d275dbd873840c66b307a687bf8975406fec..5b06816851414af4a438f298ac119d78a2b51931 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts +++ b/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts @@ -6,19 +6,17 @@ import { BrapiService } from '../brapi.service'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { of } from 'rxjs'; -import { - BrapiDescriptor, - BrapiDonor, - BrapiGermplasmAttributes, - BrapiGermplasmPedigree, - BrapiGermplasmProgeny, - BrapiSet, - BrapiSibling -} from '../models/brapi.germplasm.model'; -import { Germplasm, GermplasmData, GermplasmResult, Institute, Origin, Site } from '../models/gnpis.germplasm.model'; + import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; import { MomentModule } from 'ngx-moment'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; +import { CardSectionComponent } from '../card-section/card-section.component'; +import { CardRowComponent } from '../card-row/card-row.component'; +import { CardTableComponent } from '../card-table/card-table.component'; +import { MapComponent } from '../map/map.component'; +import { BrapiGermplasmAttributes, BrapiGermplasmPedigree, BrapiResult, BrapiSibling } from '../models/brapi.model'; +import { Donor, Germplasm, GermplasmInstitute, GermplasmSet, Institute, Site } from '../models/gnpis.model'; +import { DataDiscoverySource } from '../models/data-discovery.model'; import { MockComponent } from 'ng-mocks'; import { XrefsComponent } from '../xrefs/xrefs.component'; @@ -36,15 +34,15 @@ describe('GermplasmCardComponent', () => { return this.element('h3'); } - get headerTitle() { - return this.elements('.headerTitle'); + get cardHeader() { + return this.elements('div.card-header'); } } const brapiService = jasmine.createSpyObj( 'BrapiService', [ 'germplasm', - 'germplasmProgeny', + /*'germplasmProgeny',*/ 'germplasmPedigree', 'germplasmAttributes' ] @@ -53,11 +51,12 @@ describe('GermplasmCardComponent', () => { const gnpisService = jasmine.createSpyObj( 'GnpisService', [ 'germplasm', - 'germplasmByPuid' + 'germplasmByPuid', + 'getSource' ] ); - const brapiSite: Site = { + const gnpisSite: Site = { latitude: null, longitude: null, siteId: null, @@ -70,13 +69,9 @@ describe('GermplasmCardComponent', () => { defaultDisplayName: 'frere1' }; - const brapiDescriptor: BrapiDescriptor = { - name: 'caracteristique1', - pui: '12', - value: '32' - }; - const brapiGermplasmPedigree: GermplasmResult<BrapiGermplasmPedigree> = { + const brapiGermplasmPedigree: BrapiResult<BrapiGermplasmPedigree> = { + metadata: null, result: { germplasmDbId: '12', defaultDisplayName: '12', @@ -94,15 +89,15 @@ describe('GermplasmCardComponent', () => { } }; - const brapiGermplasmProgeny: GermplasmResult<BrapiGermplasmProgeny> = { + /*const brapiGermplasmProgeny: GermplasmResult<BrapiGermplasmProgeny> = { result: { germplasmDbId: '11', defaultDisplayName: '11', progeny: [brapiSibling] } - }; + };*/ - const brapiInstitute: Institute = { + const gnpisInstitute: Institute = { instituteName: 'urgi', instituteCode: 'inra', acronym: 'urgi', @@ -113,9 +108,9 @@ describe('GermplasmCardComponent', () => { logo: null }; - const brapiOrigin: Origin = { - institute: brapiInstitute, - germplasmPUI: '12', + const gnpisGermplasmInstitute: GermplasmInstitute = { + ...gnpisInstitute, + institute: gnpisInstitute, accessionNumber: '12', accessionCreationDate: '1993', materialType: 'feuille', @@ -125,14 +120,15 @@ describe('GermplasmCardComponent', () => { distributionStatus: null }; - const brapiDonor: BrapiDonor = { - donorInstitute: brapiInstitute, - germplasmPUI: '12', - accessionNumber: '12', - donorInstituteCode: 'urgi' + const gnpisDonor: Donor = { + donorInstitute: gnpisInstitute, + donorGermplasmPUI: '12', + donorAccessionNumber: '12', + donorInstituteCode: 'urgi', + donationDate: null }; - const brapiSet: BrapiSet = { + const gnpisGermplasmSet: GermplasmSet = { germplasmCount: 12, germplasmRef: null, id: 12, @@ -140,18 +136,21 @@ describe('GermplasmCardComponent', () => { type: 'plan' }; - const brapiGermplasmAttributes: GermplasmResult<GermplasmData<BrapiGermplasmAttributes[]>> = { + const brapiGermplasmAttributes: BrapiResult<BrapiGermplasmAttributes> = { + metadata: null, result: { + germplasmDbId: '12', data: [{ attributeName: 'longueur', - value: '30' + value: '30', + attributeDbId: '1', + attributeCode: 'longeur', + determinedDate: 'today' }] } }; const germplasmTest: Germplasm = { - url: 'www.cirad.fr', - source: 'cirad', germplasmDbId: 'test', defaultDisplayName: 'test', accessionNumber: 'test', @@ -172,7 +171,7 @@ describe('GermplasmCardComponent', () => { speciesAuthority: 'L', subtaxa: null, subtaxaAuthority: null, - donors: [brapiDonor], + donors: [gnpisDonor], acquisitionDate: null, genusSpecies: null, genusSpeciesSubtaxa: null, @@ -181,34 +180,38 @@ describe('GermplasmCardComponent', () => { geneticNature: null, comment: null, photo: null, - holdingInstitute: brapiInstitute, - holdingGenbank: brapiInstitute, + holdingInstitute: gnpisInstitute, + holdingGenbank: gnpisInstitute, presenceStatus: null, children: null, - descriptors: [brapiDescriptor], originSite: null, collectingSite: null, evaluationSites: null, - collector: brapiOrigin, - breeder: brapiOrigin, - distributors: [brapiOrigin], - panel: [brapiSet], - collection: [brapiSet], - population: [brapiSet] + collector: gnpisGermplasmInstitute, + breeder: gnpisGermplasmInstitute, + distributors: [gnpisGermplasmInstitute], + panel: [gnpisGermplasmSet], + collection: [gnpisGermplasmSet], + population: [gnpisGermplasmSet] }; - const germplasmResultTest = { - result: germplasmTest + const source: DataDiscoverySource = { + '@id': 'src1', + '@type': ['schema:DataCatalog'], + 'schema:identifier': 'srcId', + 'schema:name': 'source1', + 'schema:url': 'srcUrl', + 'schema:image': null }; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule, NgbPopoverModule, MomentModule], declarations: [ - GermplasmCardComponent, LoadingSpinnerComponent, MockComponent(XrefsComponent) + GermplasmCardComponent, LoadingSpinnerComponent, MockComponent(XrefsComponent), CardSectionComponent, + CardRowComponent, LoadingSpinnerComponent, CardTableComponent, MapComponent ], providers: [ - // { provide: ActivatedRoute, useValue: activatedRoute }, { provide: BrapiService, useValue: brapiService }, { provide: GnpisService, useValue: gnpisService }, { @@ -228,7 +231,8 @@ describe('GermplasmCardComponent', () => { gnpisService.germplasm.and.returnValue(of(germplasmTest)); gnpisService.germplasmByPuid.and.returnValue(of(germplasmTest)); - brapiService.germplasmProgeny.and.returnValue(of(brapiGermplasmProgeny)); + gnpisService.getSource.and.returnValue(of(source)); + /*brapiService.germplasmProgeny.and.returnValue(of(brapiGermplasmProgeny));*/ brapiService.germplasmPedigree.and.returnValue(of(brapiGermplasmPedigree)); brapiService.germplasmAttributes.and.returnValue(of(brapiGermplasmAttributes)); @@ -240,10 +244,13 @@ describe('GermplasmCardComponent', () => { expect(component.germplasmGnpis).toBeTruthy(); tester.detectChanges(); expect(tester.title).toContainText('Germplasm: test'); - expect(tester.headerTitle[0]).toContainText('Identification'); - expect(tester.headerTitle[1]).toContainText('Holding'); - expect(tester.headerTitle[2]).toContainText('Breeder'); - expect(tester.headerTitle[3]).toContainText('Collecting'); + expect(tester.cardHeader[0]).toContainText('Identification'); + expect(tester.cardHeader[1]).toContainText('Holding'); + expect(tester.cardHeader[2]).toContainText('Collecting'); + expect(tester.cardHeader[3]).toContainText('Breeder'); + expect(tester.cardHeader[4]).toContainText('Donor'); + expect(tester.cardHeader[5]).toContainText('Distributor'); + expect(tester.cardHeader[6]).toContainText('Evaluation Data'); }); })); }); diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.ts b/frontend/src/app/germplasm-card/germplasm-card.component.ts index 14fa0e9e6173dc701a1d52fe9aef089519e15861..fa665b58aea9b42828f310cafb6dfa2972da71cd 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.ts +++ b/frontend/src/app/germplasm-card/germplasm-card.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { BrapiService } from '../brapi.service'; import { GnpisService } from '../gnpis.service'; -import { Germplasm, GermplasmResult } from '../models/gnpis.germplasm.model'; -import { BrapiGermplasmAttributes, BrapiGermplasmPedigree, BrapiGermplasmProgeny } from '../models/brapi.germplasm.model'; +import { BrapiAttributeData, BrapiGermplasmPedigree, BrapiLocation } from '../models/brapi.model'; +import { Children, Germplasm, Site } from '../models/gnpis.model'; +import { DataDiscoverySource } from '../models/data-discovery.model'; @Component({ selector: 'gpds-germplasm-card', @@ -13,17 +14,35 @@ import { BrapiGermplasmAttributes, BrapiGermplasmPedigree, BrapiGermplasmProgeny export class GermplasmCardComponent implements OnInit { + alreadyInitialize = false; - constructor(private brapiService: BrapiService, private gnpisService: GnpisService, private route: ActivatedRoute) { + constructor(private brapiService: BrapiService, + private gnpisService: GnpisService, + private route: ActivatedRoute, + private router: Router) { + + this.router.events.subscribe((event: any) => { + // If it is a NavigationEnd event re-initalise the component + if (this.alreadyInitialize && event instanceof NavigationEnd) { + this.ngOnInit(); + } + }); } germplasmGnpis: Germplasm; - germplasmPedigree: GermplasmResult<BrapiGermplasmPedigree>; - germplasmProgeny: GermplasmResult<BrapiGermplasmProgeny>; - germplasmAttributes: BrapiGermplasmAttributes[]; + germplasmPedigree: BrapiGermplasmPedigree; + germplasmProgeny: Children[]; + germplasmAttributes: BrapiAttributeData[]; + germplasmLocations: BrapiLocation[] = []; germplasmId: string; germplasmPuid: string; - IMAGES_SIREGAL_URL = 'https://urgi.versailles.inra.fr/files/siregal/images/accession'; + germplasmSource: DataDiscoverySource; + + // TODO extract those url in a configuration file. + IMAGES_ACCESSION_URL = 'https://urgi.versailles.inra.fr/files/siregal/images/accession/'; + IMAGES_INSTITUTION_URL = 'https://urgi.versailles.inra.fr/files/siregal/images//institution/'; + IMAGES_BRC_URL = 'https://urgi.versailles.inra.fr/files/siregal/images/grc/inra_brc_en.png'; + loaded: Promise<any>; loading = true; @@ -31,30 +50,31 @@ export class GermplasmCardComponent implements OnInit { ngOnInit() { - // console.log(this.route.snapshot); - // console.log(this.route); this.germplasmId = this.route.snapshot.queryParams.id; this.germplasmPuid = this.route.snapshot.queryParams.pui; + const germplasm$ = this.getGermplasm(this.germplasmId, this.germplasmPuid); germplasm$.then(result => { const germplasmId = this.germplasmId ? this.germplasmId : result.germplasmDbId; - const germplasmProgeny$ = this.brapiService.germplasmProgeny(germplasmId).toPromise(); + + // TODO use the progeny call when the information about parent will be added. + /*const germplasmProgeny$ = this.brapiService.germplasmProgeny(germplasmId).toPromise(); germplasmProgeny$ .then(germplasmProgeny => { - this.germplasmProgeny = germplasmProgeny; - }); + this.germplasmProgeny = germplasmProgeny.result; + });*/ const germplasmPedigree$ = this.brapiService.germplasmPedigree(germplasmId).toPromise(); germplasmPedigree$ .then(germplasmPedigree => { - this.germplasmPedigree = germplasmPedigree; + this.germplasmPedigree = germplasmPedigree.result; }); const germplasmAttributes$ = this.brapiService.germplasmAttributes(germplasmId).toPromise(); germplasmAttributes$ .then(germplasmAttributes => { - if (germplasmAttributes.result) { - this.germplasmAttributes = germplasmAttributes.result.data; + if (germplasmAttributes.result.data) { + this.germplasmAttributes = germplasmAttributes.result.data.sort(this.compareAttributes); } }); @@ -63,6 +83,7 @@ export class GermplasmCardComponent implements OnInit { this.loaded = Promise.all([germplasm$]); this.loaded.then(() => { this.loading = false; + this.alreadyInitialize = true; }); } @@ -71,36 +92,157 @@ export class GermplasmCardComponent implements OnInit { let germplasm$: Promise<Germplasm>; if (id) { germplasm$ = this.gnpisService.germplasm(id).toPromise(); - germplasm$ - .then(germplasmGnpis => { - this.germplasmGnpis = germplasmGnpis; - }); } else { germplasm$ = this.gnpisService.germplasmByPuid(pui).toPromise(); - germplasm$ - .then(germplasmGnpis => { - this.germplasmGnpis = germplasmGnpis; - }); } + germplasm$ + .then(germplasmGnpis => { + this.germplasmGnpis = germplasmGnpis; + // Get germplasm source + const sourceURI = germplasmGnpis['schema:includedInDataCatalog']; + this.getGermplasmSource(sourceURI); + this.reformatData(germplasmGnpis); + }); return germplasm$; } - testProgeny() { + // TODO Remove the condition when the field includedInDataCatalog will be added to URGI study. + getGermplasmSource(sourceURI: string) { + if (sourceURI) { + const source$ = this.gnpisService.getSource(sourceURI); + source$ + .subscribe(src => { + this.germplasmSource = src; + }); + } else { + const urgiURI = 'https://urgi.versailles.inra.fr'; + const source$ = this.gnpisService.getSource(urgiURI); + source$ + .subscribe(src => { + this.germplasmSource = src; + }); + } + } + + + reformatData(germplasmGnpis) { + if (germplasmGnpis.children) { + this.germplasmProgeny = germplasmGnpis.children.sort(); + } + if (germplasmGnpis.donors) { + this.germplasmGnpis.donors.sort(this.compareDonorInstitutes); + } + if (germplasmGnpis.collection) { + this.germplasmGnpis.collection.sort(this.compareCollectionPanel); + } + if (this.germplasmGnpis.panel) { + this.germplasmGnpis.panel.sort(this.compareCollectionPanel); + } + if (this.germplasmGnpis.collectingSite) { + this.siteToBrapiLocation(this.germplasmGnpis.collectingSite); + } + if (this.germplasmGnpis.originSite) { + this.siteToBrapiLocation(this.germplasmGnpis.originSite); + } + if (this.germplasmGnpis.evaluationSites && this.germplasmGnpis.evaluationSites.length > 0) { + for (const site of this.germplasmGnpis.evaluationSites) { + this.siteToBrapiLocation(site); + } + } + } + + + siteToBrapiLocation(site: Site) { + if (site && site.siteId && site.latitude && site.longitude) { + this.germplasmLocations.push({ + locationDbId: site.siteId, + locationName: site.siteName, + locationType: site.siteType, + latitude: site.latitude, + longitude: site.longitude + }); + } + } + + // TODO: use a generic function to get path in object (or null if non-existent) + checkProgeny() { return (this.germplasmProgeny - && this.germplasmProgeny.result - && this.germplasmProgeny.result.progeny - && this.germplasmProgeny.result.progeny.length > 0); + && this.germplasmProgeny.length > 0); } - testPedigree() { + checkBreeder() { + return (this.germplasmGnpis.breeder) + && ((this.germplasmGnpis.breeder.institute && this.germplasmGnpis.breeder.institute.instituteName) + || this.germplasmGnpis.breeder.accessionCreationDate + || this.germplasmGnpis.breeder.accessionNumber + || this.germplasmGnpis.breeder.registrationYear + || this.germplasmGnpis.breeder.deregistrationYear); + } + + checkPedigree() { return (this.germplasmPedigree - && this.germplasmPedigree.result - && (this.germplasmPedigree.result.parent1Name - || this.germplasmPedigree.result.parent2Name - || this.germplasmPedigree.result.crossingPlan - || this.germplasmPedigree.result.crossingYear - || this.germplasmPedigree.result.familyCode) + && (this.germplasmPedigree.parent1Name + || this.germplasmPedigree.parent2Name + || this.germplasmPedigree.crossingPlan + || this.germplasmPedigree.crossingYear + || this.germplasmPedigree.familyCode) ); } -} + + checkCollectorInstituteObject() { + return ( + this.germplasmGnpis.collector + && this.germplasmGnpis.collector.institute + && this.germplasmGnpis.collector.institute.instituteName); + } + + checkCollectorInstituteFields() { + return (this.germplasmGnpis.collector) && + (this.germplasmGnpis.collector.accessionNumber + || this.germplasmGnpis.collector.accessionCreationDate + || this.germplasmGnpis.collector.materialType + || this.germplasmGnpis.collector.collectors + || this.germplasmGnpis.collector.registrationYear + || this.germplasmGnpis.collector.deregistrationYear + || this.germplasmGnpis.collector.distributionStatus + ); + } + + checkOriginCollecting() { + + return (this.germplasmGnpis.originSite && this.germplasmGnpis.originSite.siteName) + || (this.germplasmGnpis.collectingSite && this.germplasmGnpis.collectingSite.siteName) + || (this.checkCollectorInstituteObject() || this.checkCollectorInstituteFields()); + } + + compareDonorInstitutes(a, b) { + if (a.donorInstitute.instituteName < b.donorInstitute.instituteName) { + return -1; + } + if (a.donorInstitute.instituteName > b.donorInstitute.instituteName) { + return 1; + } + return 0; + } + + compareAttributes(a, b) { + if (a.attributeName < b.attributeName) { + return -1; + } + if (a.attributeName > b.attributeName) { + return 1; + } + return 0; + } + + compareCollectionPanel(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + } +} diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index 512deeceddd8c1b12f011cbe8015d69d99e09566..04deb8ddc41c26ba9a63cdf36b4240ca99b0f7e4 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -1,11 +1,9 @@ -import { Germplasm, Institute, Origin, Site } from './models/gnpis.germplasm.model'; - -import { BASE_URL, BASE_URL_GERMPLASM, GnpisService } from './gnpis.service'; +import { BASE_URL, GnpisService } from './gnpis.service'; import { BrapiMetaData, BrapiResults } from './models/brapi.model'; import { DataDiscoveryCriteria, DataDiscoverySource } from './models/data-discovery.model'; -import { BrapiDescriptor, BrapiDonor, BrapiSet } from './models/brapi.germplasm.model'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; +import { Donor, Germplasm, GermplasmInstitute, GermplasmSet, Institute, Site } from './models/gnpis.model'; describe('GnpisService', () => { @@ -29,17 +27,11 @@ describe('GnpisService', () => { const site: Site = { latitude: null, longitude: null, - siteId: 1, + siteId: '1', siteName: 'Nantes', siteType: null }; - const brapiDescriptor: BrapiDescriptor = { - name: 'caracteristique1', - pui: '12', - value: '32' - }; - const brapiInstitute: Institute = { instituteName: 'urgi', instituteCode: 'inra', @@ -51,9 +43,9 @@ describe('GnpisService', () => { logo: null }; - const brapiOrigin: Origin = { + const germplasmInstitute: GermplasmInstitute = { + ...brapiInstitute, institute: brapiInstitute, - germplasmPUI: '12', accessionNumber: '12', accessionCreationDate: '1993', materialType: 'feuille', @@ -63,14 +55,15 @@ describe('GnpisService', () => { distributionStatus: null }; - const brapiDonor: BrapiDonor = { + const brapiDonor: Donor = { donorInstitute: brapiInstitute, - germplasmPUI: '12', - accessionNumber: '12', - donorInstituteCode: 'urgi' + donorGermplasmPUI: '12', + donorAccessionNumber: '12', + donorInstituteCode: 'urgi', + donationDate: null }; - const brapiSet: BrapiSet = { + const germplasmSet: GermplasmSet = { germplasmCount: 12, germplasmRef: null, id: 12, @@ -79,8 +72,6 @@ describe('GnpisService', () => { }; const germplasmTest: Germplasm = { - url: 'www.cirad.fr', - source: 'cirad', germplasmDbId: 'test', defaultDisplayName: 'test', accessionNumber: 'test', @@ -114,16 +105,15 @@ describe('GnpisService', () => { holdingGenbank: brapiInstitute, presenceStatus: null, children: null, - descriptors: [brapiDescriptor], originSite: site, collectingSite: null, evaluationSites: null, - collector: brapiOrigin, - breeder: brapiOrigin, - distributors: [brapiOrigin], - panel: [brapiSet], - collection: [brapiSet], - population: [brapiSet] + collector: germplasmInstitute, + breeder: germplasmInstitute, + distributors: [germplasmInstitute], + panel: [germplasmSet], + collection: [germplasmSet], + population: [germplasmSet] }; let gnpisService: GnpisService; @@ -144,7 +134,7 @@ describe('GnpisService', () => { const req = http.expectOne({ method: 'GET', - url: `${BASE_URL}/sources` + url: `${BASE_URL}/datadiscovery/sources` }); req.flush(sources); @@ -167,7 +157,7 @@ describe('GnpisService', () => { }); const req = http.expectOne({ - url: `${BASE_URL}/suggest?field=${field}&text=${text}&fetchSize=${fetchSize}`, + url: `${BASE_URL}/datadiscovery/suggest?field=${field}&text=${text}&fetchSize=${fetchSize}`, method: 'POST' }); req.flush(expectedSuggestions); @@ -183,7 +173,7 @@ describe('GnpisService', () => { gnpisService.germplasm(germplasmDbId).subscribe(response => { fetchedGermplasm = response; }); - http.expectOne(`${BASE_URL_GERMPLASM}/germplasm?id=${germplasmDbId}`) + http.expectOne(`${BASE_URL}/germplasm?id=${germplasmDbId}`) .flush(germplasmTest); expect(fetchedGermplasm).toEqual(germplasmTest); @@ -223,7 +213,7 @@ describe('GnpisService', () => { }); const req = http.expectOne({ - url: `${BASE_URL}/search`, + url: `${BASE_URL}/datadiscovery/search`, method: 'POST' }); req.flush(rawResult); diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index 1a7cee829791e7c9c2517227e27220e3142bc895..7b47607f9c23869a13a08e07d9dc942db04b2860 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -1,15 +1,14 @@ import { Injectable } from '@angular/core'; import { Observable, ReplaySubject, zip } from 'rxjs'; import { HttpClient } from '@angular/common/http'; -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 { Germplasm } from './models/gnpis.model'; import { XrefResponse } from './models/xref.model'; -export const BASE_URL = 'gnpis/v1/datadiscovery'; -export const BASE_URL_GERMPLASM = 'gnpis/v1'; +export const BASE_URL = 'gnpis/v1'; @Injectable({ providedIn: 'root' @@ -22,7 +21,7 @@ export class GnpisService { } private fetchSources(): void { - this.http.get(`${BASE_URL}/sources`).subscribe( + this.http.get(`${BASE_URL}/datadiscovery/sources`).subscribe( (response: BrapiResults<DataDiscoverySource>) => { const sourceByURI = {}; for (const source of response.result.data) { @@ -49,7 +48,7 @@ export class GnpisService { ): Observable<string[]> { const params = { field, text, fetchSize: fetchSize.toString() }; return this.http.post<string[]>( - `${BASE_URL}/suggest`, criteria, { params } + `${BASE_URL}/datadiscovery/suggest`, criteria, { params } ); } @@ -65,7 +64,7 @@ export class GnpisService { // Get source by URI this.sourceByURI$, // Get documents by criteria - this.http.post<any>(`${BASE_URL}/search`, criteria) + this.http.post<any>(`${BASE_URL}/datadiscovery/search`, criteria) ).pipe(map(([sourceByURI, response]) => { // Extract BrAPI documents from result const documents = response.result.data; @@ -94,11 +93,11 @@ export class GnpisService { } germplasm(germplasmDbId: string): Observable<Germplasm> { - return this.http.get<Germplasm>(`${BASE_URL_GERMPLASM}/germplasm?id=${germplasmDbId}`); + return this.http.get<Germplasm>(`${BASE_URL}/germplasm?id=${germplasmDbId}`); } germplasmByPuid(pui: string): Observable<Germplasm> { - return this.http.get<Germplasm>(`${BASE_URL_GERMPLASM}/germplasm?pui=${pui}`); + return this.http.get<Germplasm>(`${BASE_URL}/germplasm?pui=${pui}`); } /** @@ -109,7 +108,7 @@ export class GnpisService { } xref(xrefId: string): Observable<XrefResponse> { - return this.http.get<XrefResponse>(`gnpis/v1/xref/documentbyfulltextid?linkedRessourcesID=${xrefId}`); + return this.http.get<XrefResponse>(`${BASE_URL}/xref/documentbyfulltextid?linkedRessourcesID=${xrefId}`); } } diff --git a/frontend/src/app/map/map.component.html b/frontend/src/app/map/map.component.html index d6143c137693b3d868a4eedde59cad686e1a3c64..9285cb1a709ff23a3253d2e6357d8678d3ea0215 100644 --- a/frontend/src/app/map/map.component.html +++ b/frontend/src/app/map/map.component.html @@ -1,6 +1,6 @@ <div id="map" class="rounded"> </div> -<div id="maplegend"> +<div *ngIf="curatedLocationList.length > 0" id="maplegend"> <img src="assets/gpds/images/marker-icon-red.png" id="red"/> <label for="red">Origin site</label> <img src="assets/gpds/images/marker-icon-blue.png" id="blue"/> diff --git a/frontend/src/app/map/map.component.ts b/frontend/src/app/map/map.component.ts index df746dd8cca185791976a68885dac0e454c1369c..bc566b84667bf0f2cf3e74d1ce99e832df0877e4 100644 --- a/frontend/src/app/map/map.component.ts +++ b/frontend/src/app/map/map.component.ts @@ -11,17 +11,19 @@ import { BrapiLocation } from '../models/brapi.model'; export class MapComponent implements OnInit { @Input() locations: BrapiLocation[]; + curatedLocationList: BrapiLocation[] = []; constructor() { } ngOnInit() { - const container = L.DomUtil.get('map'); - if (container) { + + this.removeEmptyLocations(this.locations); + if (this.curatedLocationList.length > 0) { const map = L.map('map'); // initialize map centered on the first site - const firstLocation: BrapiLocation = this.locations[0]; + const firstLocation: BrapiLocation = this.curatedLocationList[0]; if (firstLocation) { map.setView([firstLocation.latitude, firstLocation.longitude], 5); } @@ -30,9 +32,10 @@ export class MapComponent implements OnInit { attribution: 'Tiles © Esri — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, ' + 'Esri China (Hong Kong), Esri (Thailand), TomTom, 2012' }).addTo(map); + // add markers for all locations using markercluster plugin const markers = new MarkerClusterGroup(); - for (const location of this.locations) { + for (const location of this.curatedLocationList) { const icon = L.icon({ iconUrl: this.getMarkerIconUrl(location) }); @@ -49,9 +52,12 @@ export class MapComponent implements OnInit { ); } map.addLayer(markers); + } else { + L.DomUtil.get('map').remove(); } } + getMarkerIconUrl(site: BrapiLocation): string { if (site.locationType === 'Origin site') { return 'assets/gpds/images/marker-icon-red.png'; @@ -64,4 +70,12 @@ export class MapComponent implements OnInit { } return 'assets/gpds/images/marker-icon-purple.png'; } + + removeEmptyLocations(locations: BrapiLocation[]) { + for (const location of locations) { + if (location && location.latitude && location.longitude) { + this.curatedLocationList.push(location); + } + } + } } diff --git a/frontend/src/app/models/brapi.germplasm.model.ts b/frontend/src/app/models/brapi.germplasm.model.ts deleted file mode 100644 index 54ed2d04026b9b78b22ed7e298a976b6ebeddb7d..0000000000000000000000000000000000000000 --- a/frontend/src/app/models/brapi.germplasm.model.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Institute } from './gnpis.germplasm.model'; - - -export interface BrapiSibling { - germplasmDbId: string; - defaultDisplayName: string; -} - -export interface BrapiDescriptor { - name: string; - pui: string; - value: string; -} - -export interface BrapiGermplasmPedigree { - germplasmDbId: string; - defaultDisplayName: string; - pedigree: string; - crossingPlan: string; - crossingYear: string; - familyCode: string; - parent1DbId: string; - parent1Name: string; - parent1Type: string; - parent2DbId: string; - parent2Name: string; - parent2Type: string; - siblings: BrapiSibling[]; -} - -export interface BrapiGermplasmProgeny { - germplasmDbId: string; - defaultDisplayName: string; - progeny: BrapiSibling[]; -} - -export interface BrapiGermplasmAttributes { - attributeName: string; - value: string; -} - -export interface BrapiDonor { - donorInstitute: Institute; - germplasmPUI: string; - accessionNumber: string; - donorInstituteCode: string; -} - -export interface BrapiSet { - germplasmCount: number; - germplasmRef: string; - id: number; - name: string; - type: string; -} diff --git a/frontend/src/app/models/brapi.model.ts b/frontend/src/app/models/brapi.model.ts index 4360105b11800d1bcf052540a0526e112bd3ec75..fa9963801a1053877cf35102541478c6b81081c4 100644 --- a/frontend/src/app/models/brapi.model.ts +++ b/frontend/src/app/models/brapi.model.ts @@ -14,7 +14,7 @@ export interface BrapiResult<T> { } -interface BrapiData<T> { +export interface BrapiData<T> { data: T[]; } @@ -42,7 +42,7 @@ export interface BrapiStudy extends BrapiHasDocumentationURL { trialDbIds: string[]; location: BrapiLocation; contacts: BrapiContacts[]; - additionalInfo: AdditionalInfo; + additionalInfo: BrapiAdditionalInfo; dataLinks: { name: string; type: string; @@ -51,36 +51,36 @@ export interface BrapiStudy extends BrapiHasDocumentationURL { } +export interface BrapiContacts { + contactDbId: string; + name: string; + email: string; + type: string; + institutionName: string; +} + + export interface BrapiLocation extends BrapiHasDocumentationURL { locationDbId: string; locationName: string; locationType: string; - abbreviation: string; - countryCode: string; - countryName: string; - institutionAddress: string; - institutionName: string; - altitude: number; + abbreviation?: string; + countryCode?: string; + countryName?: string; + institutionAddress?: string; + institutionName?: string; + altitude?: number; latitude: number; longitude: number; - additionalInfo?: AdditionalInfo; + additionalInfo?: BrapiAdditionalInfo; } -export interface AdditionalInfo { +export interface BrapiAdditionalInfo { [key: string]: string; } -export interface BrapiContacts { - contactDbId: string; - name: string; - email: string; - type: string; - institutionName: string; -} - - export interface BrapiObservationVariable extends BrapiHasDocumentationURL { observationVariableDbId: string; contextOfUse: string[]; @@ -99,23 +99,99 @@ export interface BrapiObservationVariable extends BrapiHasDocumentationURL { } +export interface BrapiTrial extends BrapiHasDocumentationURL { + trialDbId: string; + trialName: string; + trialType: string; + active: boolean; + studies: { + studyDbId: string; + studyName: string; + }[]; +} + export interface BrapiGermplasm extends BrapiHasDocumentationURL { germplasmDbId: string; + defaultDisplayName: string; accessionNumber: string; germplasmName: string; + germplasmPUI: string; + pedigree: string; + seedSource: string; + synonyms: string[]; + commonCropName: string; + instituteCode: string; + instituteName: string; + biologicalStatusOfAccessionCode: string; + countryOfOriginCode: string; + typeOfGermplasmStorageCode: string[]; + taxonIds: BrapiTaxonIds[]; genus: string; species: string; + speciesAuthority: string; subtaxa: string; + subtaxaAuthority: string; + donors: BrapiDonor[]; + acquisitionDate: string; } -export interface BrapiTrial extends BrapiHasDocumentationURL { - trialDbId: string; - trialName: string; - trialType: string; - active: boolean; - studies: { - studyDbId: string; - studyName: string; - }[]; +export interface BrapiTaxonIds { + sourceName: string; + taxonId: string; +} + +export interface BrapiDonor { + donorGermplasmPUI: string; + donorAccessionNumber: string; + donorInstituteCode: string; + donationDate: number; +} + +export interface BrapiGermplasmPedigree { + germplasmDbId: string; + defaultDisplayName: string; + pedigree: string; + crossingPlan: string; + crossingYear: string; + familyCode: string; + parent1DbId: string; + parent1Name: string; + parent1Type: string; + parent2DbId: string; + parent2Name: string; + parent2Type: string; + siblings: BrapiSibling[]; +} + +export interface BrapiSibling { + germplasmDbId: string; + defaultDisplayName: string; +} + +export interface BrapiGermplasmProgeny { + germplasmDbId: string; + defaultDisplayName: string; + progeny: BrapiProgeny[]; +} + +export interface BrapiProgeny { + germplasmDbId: string; + defaultDisplayName: string; + parentType: string; + +} + + +export interface BrapiGermplasmAttributes { + germplasmDbId: string; + data: BrapiAttributeData[]; +} + +export interface BrapiAttributeData { + attributeDbId: string; + attributeName: string; + attributeCode: string; + value: string; + determinedDate: string; } diff --git a/frontend/src/app/models/gnpis.germplasm.model.ts b/frontend/src/app/models/gnpis.germplasm.model.ts deleted file mode 100644 index a18cbb87f9d78d12db5d14e026033e6ba354594d..0000000000000000000000000000000000000000 --- a/frontend/src/app/models/gnpis.germplasm.model.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BrapiDescriptor, BrapiDonor, BrapiSet } from './brapi.germplasm.model'; - -export interface Site { - latitude: number; - longitude: number; - siteId: number; - siteName: string; - siteType: string; -} - -export interface Germplasm { - source: string; - url: string; - germplasmDbId: string; - defaultDisplayName: string; - accessionNumber: string; - germplasmName: string; - germplasmPUI: string; - pedigree: string; - seedSource: string; - synonyms: string; - commonCropName: string; - instituteCode: string; - instituteName: string; - biologicalStatusOfAccessionCode: string; - countryOfOriginCode: string; - typeOfGermplasmStorageCode: string; - taxonIds: string; - genus: string; - species: string; - speciesAuthority: string; - subtaxa: string; - subtaxaAuthority: string; - donors: BrapiDonor[]; - acquisitionDate: string; - genusSpecies: string; - genusSpeciesSubtaxa: string; - taxonSynonyms: string[]; - taxonCommonNames: string[]; - geneticNature: string; - comment: string; - photo: string; - holdingInstitute: Institute; - holdingGenbank: Institute; - presenceStatus: string; - children: string; - descriptors: BrapiDescriptor[]; - originSite: Site; - collectingSite: Site; - evaluationSites: Site[]; - collector: Origin; - breeder: Origin; - distributors: Origin[]; - panel: BrapiSet[]; - collection: BrapiSet[]; - population: BrapiSet[]; -} - -export interface Origin { - institute: Institute; - germplasmPUI: string; - accessionNumber: string; - accessionCreationDate: string; - materialType: string; - collectors: string; - registrationYear: string; - deregistrationYear: string; - distributionStatus: string; -} - -export interface Institute { - instituteName: string; - instituteCode: string; - acronym: string; - organisation: string; - instituteType: string; - webSite: string; - address: string; - logo: string; -} - -export interface GermplasmData<T> { - data: T; -} - -export interface GermplasmResult<T> { - result: T; -} - - - - diff --git a/frontend/src/app/models/gnpis.model.ts b/frontend/src/app/models/gnpis.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..061c0d2a4b6429c78cbbad30589b13f67ac8c2cd --- /dev/null +++ b/frontend/src/app/models/gnpis.model.ts @@ -0,0 +1,92 @@ +import { BrapiDonor, BrapiGermplasm } from './brapi.model'; + +export interface Germplasm extends BrapiGermplasm { + genusSpecies: string; + genusSpeciesSubtaxa: string; + taxonSynonyms: string[]; + taxonCommonNames: string[]; + geneticNature: string; + comment: string; + photo: Photo; + holdingInstitute: Institute; + holdingGenbank: Institute; + presenceStatus: string; + children: Children[]; + originSite: Site; + collectingSite: Site; + evaluationSites: Site[]; + collector: GermplasmInstitute; + breeder: GermplasmInstitute; + donors: Donor[]; + distributors: GermplasmInstitute[]; + panel: GermplasmSet[]; + collection: GermplasmSet[]; + population: GermplasmSet[]; +} + +export interface Site { + latitude: number; + longitude: number; + siteId: string; + siteName: string; + siteType: string; +} + +export interface Institute { + instituteName: string; + instituteCode: string; + acronym: string; + organisation: string; + instituteType: string; + webSite: string; + address: string; + logo: string; +} + +export interface GermplasmInstitute extends Institute { + institute: Institute; + accessionNumber: string; + accessionCreationDate: string; + materialType: string; + collectors: string; + registrationYear: string; + deregistrationYear: string; + distributionStatus?: string; +} + +export interface Photo { + copyright: string; + description: string; + fileName: string; + photoName: string; + thumbnailFileName: string; + +} + +export interface Children { + firstParentName: string; + firstParentPUI: string; + secondParentName: string; + secondParentPUI: string; + sibblings: { + name: string; + pui: string; + }[]; + +} + +export interface Donor extends BrapiDonor { + donorInstitute: Institute; +} + + +export interface GermplasmSet { + id: number; + name: string; + type: string; + germplasmCount: number; + germplasmRef: { + pui: string; + name: string; + }; +} diff --git a/frontend/src/app/navbar/navbar.component.html b/frontend/src/app/navbar/navbar.component.html index dca9fd2726401b4228b839f68ba149e2ea71bad4..0f8b7fc4b98483cbef0eb65782b9d262c336e115 100644 --- a/frontend/src/app/navbar/navbar.component.html +++ b/frontend/src/app/navbar/navbar.component.html @@ -1,4 +1,4 @@ -<nav class="navbar navbar-expand-md navbar-dark py-0 mb-5"> +<nav class="navbar navbar-expand-md navbar-dark py-0 mb-4"> <div class="container px-0"> <a class="navbar-brand d-flex align-items-center mr-0 px-2" routerLink="/"> <img src="assets/logo.png" alt="Logo" title="{{ navbar.title }}" height="60px"/> @@ -38,7 +38,6 @@ </div> </div> </div> - </li> </ul> </div> diff --git a/frontend/src/app/navbar/navbar.component.scss b/frontend/src/app/navbar/navbar.component.scss index 2a8c98cf98252cad58c7c6135e62433dd1871efc..75a2c2d35354e325d4569aa79a4745cfea16ef98 100644 --- a/frontend/src/app/navbar/navbar.component.scss +++ b/frontend/src/app/navbar/navbar.component.scss @@ -14,6 +14,7 @@ .navbar .navbar-nav .nav-link, .navbar .navbar-brand { color: $theme-navbar-color; height: $theme-navbar-height; + text-decoration: none; &:hover { color: $theme-navbar-hover-color; @@ -24,3 +25,4 @@ .dropdown-container { position: relative; } + diff --git a/frontend/src/app/result-page/document/document.component.html b/frontend/src/app/result-page/document/document.component.html index 9b1175eb96f50f02d16f3088d1de992c038761a8..af8a669f81926f2e134158656b97173d9b51488d 100644 --- a/frontend/src/app/result-page/document/document.component.html +++ b/frontend/src/app/result-page/document/document.component.html @@ -7,13 +7,21 @@ <a class="badge badge-source mr-2" [href]="getSourceURL()" target="_blank"> {{ getSource() }} </a> - <a class="title" *ngIf="getURL()" [href]="getURL()"> + <a class="title" *ngIf="getRouterLink() && document['@type'].includes('Phenotyping Study')" + [routerLink]="getRouterLink()"> {{ document["schema:name"] }} </a> - <a class="title" *ngIf="getRouterLink()" [routerLink]="getRouterLink()" [queryParams]="getQueryParam()"> + <a class="title" *ngIf="getRouterLink() && !document['@type'].includes('Phenotyping Study')" + [routerLink]="getRouterLink()" [queryParams]="getQueryParam()"> {{ document["schema:name"] }} </a> </h5> + + <h5> + <a class="title" *ngIf="getURL()" [href]="getURL()" target="_blank"> + ({{ document['schema:includedInDataCatalog']['schema:identifier'] }} data source link) + </a> + </h5> <span class="text-justify description"> <ng-container *ngIf="!needTruncation || opened"> {{ document["schema:description"] }} diff --git a/frontend/src/app/result-page/document/document.component.scss b/frontend/src/app/result-page/document/document.component.scss index 2ca792215dc2d003944532b271ec01da4eaa102e..fb44cb186291fce961d4deda6a34fbac2b0c3e9a 100644 --- a/frontend/src/app/result-page/document/document.component.scss +++ b/frontend/src/app/result-page/document/document.component.scss @@ -1,3 +1,4 @@ + .badge-source { background-color: #b54646; color: white; diff --git a/frontend/src/app/result-page/document/document.component.spec.ts b/frontend/src/app/result-page/document/document.component.spec.ts index 5f2d83663fc8f62cc63dbd834ad215a90f594d77..95089e9505227bec9e41574c3cf667c16bd55acd 100644 --- a/frontend/src/app/result-page/document/document.component.spec.ts +++ b/frontend/src/app/result-page/document/document.component.spec.ts @@ -71,7 +71,7 @@ describe('DocumentComponent', () => { expect(tester.source.attr('href')).toEqual('http://dco/url'); expect(tester.title).toContainText('doc_name'); - expect(tester.title.attr('href')).toEqual('http://dco/url'); + expect(tester.title.nativeElement['routerLink']).toEqual('/germplasm'); expect(tester.description).toContainText('description'); diff --git a/frontend/src/app/result-page/document/document.component.ts b/frontend/src/app/result-page/document/document.component.ts index 69d83161a159cc6fe050e42254e48197f4666d06..7d5f6b0e6974090ae6816d0567c611966d35b217 100644 --- a/frontend/src/app/result-page/document/document.component.ts +++ b/frontend/src/app/result-page/document/document.component.ts @@ -22,22 +22,29 @@ export class DocumentComponent implements OnInit { needTruncation = false; opened = false; + getURL() { return this.document['schema:url'] || ''; } + // TODO: index URGI schema:identifier like the partners + getRouterLink() { - if (!this.getURL()) { - for (const type of this.document['@type']) { - const cardUrl = DocumentComponent.CARD_TYPE[type]; - if (cardUrl === 'studies') { - return `/${cardUrl}/${this.document['schema:identifier']}`; - } - if (cardUrl === 'germplasm') { - return `/${cardUrl}`; + const urgiStudy = this.document['schema:includedInDataCatalog']['schema:url'] === 'https://urgi.versailles.inra.fr/gnpis/'; + for (const type of this.document['@type']) { + const cardUrl = DocumentComponent.CARD_TYPE[type]; + if (cardUrl === 'studies') { + if (urgiStudy) { + const studyId = this.document['@id'].replace(/urn:URGI\/study\//, ''); + return `/${cardUrl}/${studyId}`; } + return `/${cardUrl}/${this.document['schema:identifier']}`; + } + if (cardUrl === 'germplasm') { + return `/${cardUrl}`; } } + return ''; } diff --git a/frontend/src/app/site-card/site-card.component.html b/frontend/src/app/site-card/site-card.component.html index 9144f1aff1b091e95ac7394958aafdf6b09e138e..ba9d335ec0745debd040e32df55016c3e8dfdde8 100644 --- a/frontend/src/app/site-card/site-card.component.html +++ b/frontend/src/app/site-card/site-card.component.html @@ -7,17 +7,34 @@ </h3> - <gpds-map [locations]="[location]" *ngIf="location && location.latitude && location.longitude"></gpds-map> + <gpds-map [locations]="[location]"></gpds-map> <gpds-card-section header="Details"> <ng-template> - <div class="card-body"> + <div class="card-body card-section-body"> + <gpds-card-row - label="Site type" - [value]="location.locationType"> + label="Source" + [test]="location"> + <ng-template> + <a target="_blank" + [href]="locationSource['schema:url']"> + <img [src]="locationSource['schema:image']" alt="Location source image"/> + </a> + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Data source" + [test]="location && location['schema:identifier'] && location.documentationURL"> + <ng-template> + <a target="_blank" [href]="location.documentationURL"> + Link to this study on {{ location['schema:identifier'] }} + </a> + </ng-template> </gpds-card-row> <gpds-card-row @@ -26,14 +43,21 @@ </gpds-card-row> <gpds-card-row - label="Country code" - [value]="location.countryCode"> + label="Type" + [value]="location.locationType"> </gpds-card-row> <gpds-card-row - label="Country name" - [test]="location.countryName && !location.additionalInfo['Geographical location']" - [value]="location.countryName"> + label="Status" + [test]="location.additionalInfo['Site status']"> + <ng-template> + {{ location.additionalInfo['Site status'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Institution/Landowner" + [value]="location.institutionName"> </gpds-card-row> <gpds-card-row @@ -42,8 +66,45 @@ </gpds-card-row> <gpds-card-row - label="Institution name" - [value]="location.institutionAddress"> + label="Coordinates precision" + [test]="location.additionalInfo['Coordinates precision']"> + <ng-template> + {{ location.additionalInfo['Coordinates precision'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Latitude" + [value]="formatCoordinates(location.latitude, 'latitude')"> + </gpds-card-row> + + <gpds-card-row + label="Longitude" + [value]="formatCoordinates(location.longitude, 'longitude')"> + </gpds-card-row> + + <gpds-card-row + label="Geographical location" + [test]="location.additionalInfo['Geographical location']"> + <ng-template> + {{ location.additionalInfo['Geographical location'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Country name" + [test]="location.countryName && !location.additionalInfo['Geographical location']"> + <ng-template> + {{ location.countryName }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Country code" + [test]="location.countryCode && !location.additionalInfo['Geographical location']"> + <ng-template> + {{ location.countryCode }} + </ng-template> </gpds-card-row> <gpds-card-row @@ -52,31 +113,76 @@ </gpds-card-row> <gpds-card-row - label="Latitude" - [value]="location.latitude"> + label="Slope" + [value]="location.additionalInfo['Slope']"> </gpds-card-row> <gpds-card-row - label="Longitude" - [value]="location.longitude"> + label="Exposure" + [test]="location.additionalInfo['Exposure']"> + <ng-template> + {{ location.additionalInfo['Exposure'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Topography" + [test]="location.additionalInfo['Topography']"> + <ng-template> + {{ location.additionalInfo['Topography'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Environment type" + [test]="location.additionalInfo['Environment type']"> + <ng-template> + {{ location.additionalInfo['Environment type'] }} + </ng-template> </gpds-card-row> + + <gpds-card-row + label="Distance to city" + [test]="location.additionalInfo['Distance to city']"> + <ng-template> + {{ location.additionalInfo['Distance to city'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Direction from city" + [test]="location.additionalInfo['Direction from city']"> + <ng-template> + {{ location.additionalInfo['Direction from city'] }} + </ng-template> + </gpds-card-row> + + <gpds-card-row + label="Comment" + [test]="location.additionalInfo['Comment']"> + <ng-template> + {{ location.additionalInfo['Comment'] }} + </ng-template> + </gpds-card-row> + </div> </ng-template> </gpds-card-section> <gpds-card-section header="Additional information" - [test]="additionalInfos && additionalInfos.length != 0"> + [test]="additionalInfos && additionalInfos.length > 0"> <ng-template> - <gpds-card-table - [rows]="additionalInfos"> - <ng-template let-row> - <tr> - <td width="50%">{{ row.key }}</td> - <td>{{ row.value }}</td> - </tr> - </ng-template> - </gpds-card-table> + <div class="card-body card-section-body"> + + <ng-container *ngFor="let additionalInfo of additionalInfos"> + <gpds-card-row + [label]="additionalInfo.key" + [value]="additionalInfo.value"> + </gpds-card-row> + </ng-container> + + </div> </ng-template> </gpds-card-section> diff --git a/frontend/src/app/site-card/site-card.component.scss b/frontend/src/app/site-card/site-card.component.scss index 55181845b07a912a715c27dd619fd2bb24d19400..bb109da81d37b2df40a8dfdbeaaeaf748312932e 100644 --- a/frontend/src/app/site-card/site-card.component.scss +++ b/frontend/src/app/site-card/site-card.component.scss @@ -1,9 +1,12 @@ +@import "theme"; +@import '../../styles.scss'; -h3 { - font-weight: bold; - color: #0f6191; +a:not([href]):not([tabindex]) { + color: #0f6fa1; + cursor: pointer; } -a { +a:not([href]):not([tabindex]):hover { text-decoration: underline; } + 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 f33d7f9a27f679d1748082027c638a7b7c58054a..a8d0604c2b0194272e86b1977a59592419d62a93 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,8 @@ 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 { DataDiscoverySource } from '../models/data-discovery.model'; +import { GnpisService } from '../gnpis.service'; import { MockComponent } from 'ng-mocks'; import { XrefsComponent } from '../xrefs/xrefs.component'; @@ -18,6 +20,9 @@ describe('SiteCardComponent', () => { const brapiService = jasmine.createSpyObj( 'BrapiService', ['location'] ); + const gnpisService = jasmine.createSpyObj( + 'GnpisService', ['getSource'] + ); const response: BrapiResult<BrapiLocation> = { metadata: null, result: { @@ -47,6 +52,15 @@ describe('SiteCardComponent', () => { } }; + const source: DataDiscoverySource = { + '@id': 'src1', + '@type': ['schema:DataCatalog'], + 'schema:identifier': 'srcId', + 'schema:name': 'source1', + 'schema:url': 'srcUrl', + 'schema:image': null + }; + beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ @@ -56,6 +70,7 @@ describe('SiteCardComponent', () => { ], providers: [ { provide: BrapiService, useValue: brapiService }, + { provide: GnpisService, useValue: gnpisService }, { provide: ActivatedRoute, useValue: { @@ -76,6 +91,7 @@ describe('SiteCardComponent', () => { const fixture = TestBed.createComponent(SiteCardComponent); const component = fixture.componentInstance; brapiService.location.and.returnValues(of(response)); + gnpisService.getSource.and.returnValue(of(source)); fixture.detectChanges(); const element = fixture.nativeElement; expect(element.querySelector('h3').textContent).toBe(' Site: site1 '); diff --git a/frontend/src/app/site-card/site-card.component.ts b/frontend/src/app/site-card/site-card.component.ts index d892c85d2bbba0d545584126912530f94174ddd4..0705744e73ececc2b0b0bef39dad3febdcdfc859 100644 --- a/frontend/src/app/site-card/site-card.component.ts +++ b/frontend/src/app/site-card/site-card.component.ts @@ -3,6 +3,8 @@ import { BrapiService } from '../brapi.service'; import { ActivatedRoute } from '@angular/router'; import { BrapiLocation } from '../models/brapi.model'; import { KeyValueObject } from '../utils'; +import { DataDiscoverySource } from '../models/data-discovery.model'; +import { GnpisService } from '../gnpis.service'; @Component({ selector: 'gpds-site-card', @@ -12,11 +14,11 @@ import { KeyValueObject } from '../utils'; export class SiteCardComponent implements OnInit { location: BrapiLocation; + locationSource: DataDiscoverySource; additionalInfos: KeyValueObject[]; loading = true; - - constructor(private brapiService: BrapiService, private route: ActivatedRoute) { + constructor(private brapiService: BrapiService, private gnpisService: GnpisService, private route: ActivatedRoute) { } ngOnInit() { @@ -28,7 +30,23 @@ export class SiteCardComponent implements OnInit { this.location = response.result; this.additionalInfos = []; if (this.location.additionalInfo) { - this.additionalInfos = KeyValueObject.fromObject(this.location.additionalInfo); + this.manageAdditionalInfo(KeyValueObject.fromObject(this.location.additionalInfo).sort()); + } + const sourceURI = location['schema:includedInDataCatalog']; + // TODO Remove the condition when the field includedInDataCatalog will be added to URGI study. + if (sourceURI) { + const source$ = this.gnpisService.getSource(sourceURI); + source$ + .subscribe(src => { + this.locationSource = src; + }); + } else { + const urgiURI = 'https://urgi.versailles.inra.fr'; + const source$ = this.gnpisService.getSource(urgiURI); + source$ + .subscribe(src => { + this.locationSource = src; + }); } this.loading = false; } @@ -36,4 +54,53 @@ export class SiteCardComponent implements OnInit { }); } + + manageAdditionalInfo(keyValues: KeyValueObject[]) { + const forbiddenElements: String[] = [ + 'Site status', + 'Coordinates precision', + 'Slope', + 'Exposure', + 'Geographical location', + 'Distance to city', + 'Direction from city', + 'Environment type', + 'Topography', + 'Comment']; + for (const keyValue of keyValues) { + if (!forbiddenElements.includes(keyValue.key)) { + this.additionalInfos.push(keyValue); + } + } + } + + formatCoordinates(decimalDegrees: number, type: string): string { + if (decimalDegrees && type) { + const degree = Math.floor(decimalDegrees); + const decimalMinute = (decimalDegrees - degree) * 60; + const minute = Math.floor(decimalMinute); + const decimalSeconde = (decimalMinute - minute) * 60; + const seconde = Math.floor(decimalSeconde); + + let direction = ''; + if (type === 'latitude') { + if (decimalDegrees >= 0) { + direction = 'N'; + } else { + direction = 'S'; + } + } else if (type === 'longitude') { + if (decimalDegrees >= 0) { + direction = 'E'; + } else { + direction = 'W'; + } + } + return decimalDegrees + '° (' + degree + '° ' + minute + '\' ' + seconde + '\'\' ' + direction + ')'; + } else { + return null; + } + } + } + diff --git a/frontend/src/app/study-card/study-card.component.html b/frontend/src/app/study-card/study-card.component.html index 0ccf9c30277119280eb3752e06c2150aa5692c2c..358fd637efc795793496c6a048e7f1d572167550 100644 --- a/frontend/src/app/study-card/study-card.component.html +++ b/frontend/src/app/study-card/study-card.component.html @@ -6,13 +6,13 @@ </h3> <!-- Display the map --> - <gpds-map *ngIf="checkLocation(study.location)" [locations]="[study.location]"></gpds-map> + <gpds-map [locations]="[study.location]"></gpds-map> <!-- Display the study's info --> <gpds-card-section header="Identification"> <ng-template> - <div class="card-body"> + <div class="card-body card-section-body"> <gpds-card-row label="Name" @@ -21,13 +21,7 @@ <gpds-card-row label="Identifier" - [test]="study.studyDbId"> - <ng-template> - {{ study.studyDbId }} - <a target="_blank" *ngIf="studySource && study.documentationURL" [href]="study.documentationURL"> - ( Link to this study on {{ studySource["schema:identifier"] }} ) - </a> - </ng-template> + [value]="study.studyDbId"> </gpds-card-row> <gpds-card-row @@ -49,10 +43,10 @@ <gpds-card-row label="Data source" - [test]="studySource && study.documentationURL"> + [test]="studySource && studySource['schema:identifier'] && study.documentationURL"> <ng-template> - <a target="_blank" *ngIf="study.documentationURL" [href]="study.documentationURL"> - ( Link to this study on {{ studySource["schema:identifier"] }} ) + <a target="_blank" [href]="study.documentationURL"> + Link to this study on {{ studySource["schema:identifier"] }} </a> </ng-template> </gpds-card-row> @@ -108,7 +102,7 @@ [test]="study.dataLinks && study.dataLinks.length != 0"> <ng-template> <div *ngFor="let dataLink of study.dataLinks"> - <a [href]="dataLink.url"> + <a target="_blank" [href]="dataLink.url"> {{ dataLink.name }} </a> </div> @@ -165,7 +159,7 @@ <td> <ng-template #name>{{ row.observationVariableDbId }}</ng-template> <ng-template #link> - <a [href]=row.documentationURL>{{ row.observationVariableDbId }}</a> + <a target="_blank" [href]=row.documentationURL>{{ row.observationVariableDbId }}</a> </ng-template> <ng-container *ngIf="row.documentationURL; then link else name"></ng-container> </td> @@ -195,7 +189,7 @@ <td> <ng-template #name>{{ row.trialName }}</ng-template> <ng-template #link> - <a [href]=row.documentationURL>{{ row.trialName }}</a> + <a target="_blank" [href]=row.documentationURL>{{ row.trialName }}</a> </ng-template> <ng-container *ngIf="row.documentationURL; then link else name"></ng-container> </td> @@ -221,9 +215,9 @@ <gpds-card-table [headers]="[ 'Role', - 'Name', - 'Email', - 'Institution' + 'Name', + 'Email', + 'Institution' ]" [rows]="study.contacts"> <ng-template let-row> diff --git a/frontend/src/app/study-card/study-card.component.scss b/frontend/src/app/study-card/study-card.component.scss index c344730e9220180bc60b5188f46d5b683fb3932e..f0376501baaeced5c8d3194b0d2d23faff4269fe 100644 --- a/frontend/src/app/study-card/study-card.component.scss +++ b/frontend/src/app/study-card/study-card.component.scss @@ -1,8 +1,11 @@ @import "theme"; @import '../../styles.scss'; -h3 { - font-weight: bold; - color: #0f6191; +a:not([href]):not([tabindex]) { + color: #0f6fa1; + cursor: pointer; } +a:not([href]):not([tabindex]):hover { + text-decoration: underline; +} 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 e9e0e8cf56bcaff0293df0b80e4a4383494164cd..cd61029fc8ca00b54b9e7b31996f72f422c2e37f 100644 --- a/frontend/src/app/study-card/study-card.component.spec.ts +++ b/frontend/src/app/study-card/study-card.component.spec.ts @@ -48,7 +48,7 @@ describe('StudyCardComponent', () => { } get map() { - return this.element('gpds-map'); + return this.element('div #map'); } } @@ -170,18 +170,50 @@ describe('StudyCardComponent', () => { result: { data: [{ germplasmDbId: 'g1', + defaultDisplayName: 'germplam1', accessionNumber: 'G_10', germplasmName: 'germplam1', + germplasmPUI: 'urn_g1', + pedigree: null, + seedSource: 'Versaille Institute', + synonyms: null, + commonCropName: 'cheery', + instituteCode: '78', + instituteName: 'Versaille Institute', + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: 'FR', + typeOfGermplasmStorageCode: null, + taxonIds: null, genus: 'Populus', species: 'x generosa', - subtaxa: '' + speciesAuthority: 'Pop', + subtaxa: 'subsp', + subtaxaAuthority: '', + donors: null, + acquisitionDate: 'yesterday' }, { germplasmDbId: 'g2', + defaultDisplayName: 'germplam2', accessionNumber: 'G_20', germplasmName: 'germplam2', + germplasmPUI: 'urn_g2', + pedigree: null, + seedSource: 'Versaille Institute', + synonyms: null, + commonCropName: 'cheery', + instituteCode: '78', + instituteName: 'Versaille Institute', + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: 'FR', + typeOfGermplasmStorageCode: null, + taxonIds: null, genus: 'Triticum', species: 'aestivum', - subtaxa: 'subsp' + speciesAuthority: 'Trit', + subtaxa: 'subsp', + subtaxaAuthority: '', + donors: null, + acquisitionDate: 'today' }], } }; @@ -234,7 +266,8 @@ describe('StudyCardComponent', () => { expect(tester.cardHeader[0]).toContainText('Identification'); - expect(tester.studyInfo[1]).toContainText('Link to this study on srcId'); + expect(tester.studyInfo[0]).toContainText('study1'); + expect(tester.studyInfo[1]).toContainText('s1'); expect(tester.cardHeader[1]).toContainText('Genotype'); expect(component.studyGermplasms.length).toEqual(2); diff --git a/frontend/src/app/study-card/study-card.component.ts b/frontend/src/app/study-card/study-card.component.ts index a8f729b2bef6861b93a19043912a311248e5b14f..27188c0189f62ab41e9f2020ab3c8603377c0f9b 100644 --- a/frontend/src/app/study-card/study-card.component.ts +++ b/frontend/src/app/study-card/study-card.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { BrapiService } from '../brapi.service'; import { ActivatedRoute } from '@angular/router'; -import { BrapiGermplasm, BrapiLocation, BrapiObservationVariable, BrapiStudy, BrapiTrial } from '../models/brapi.model'; +import { BrapiGermplasm, BrapiObservationVariable, BrapiStudy, BrapiTrial } from '../models/brapi.model'; import { GnpisService } from '../gnpis.service'; import { DataDiscoverySource } from '../models/data-discovery.model'; @@ -36,14 +36,21 @@ export class StudyCardComponent implements OnInit { study$ .then(response => { this.study = response.result; + if (this.study.contacts) { + this.study.contacts.sort((var1, var2) => + var1.name.localeCompare(var2.name)); + } this.additionalInfos = []; if (this.study.additionalInfo) { - this.additionalInfos = KeyValueObject.fromObject(this.study.additionalInfo); + this.additionalInfos = KeyValueObject.fromObject(this.study.additionalInfo).sort(); } // Get study trials - this.trialsIds = this.study.trialDbIds; + if (this.study.trialDbIds) { + this.trialsIds = this.study.trialDbIds.sort(); + } + this.studyDataset = []; if (this.trialsIds && this.trialsIds !== []) { for (const trialsId of this.trialsIds) { @@ -57,9 +64,14 @@ export class StudyCardComponent implements OnInit { this.studyDataset.push(trial); }); } + if (this.studyDataset) { + this.studyDataset.sort((var1, var2) => + var1.trialName.localeCompare(var2.trialName)); + } } // Get study source + // TODO Remove the condition when the field includedInDataCatalog will be added to URGI study. const sourceURI = this.study['schema:includedInDataCatalog']; if (sourceURI) { const source$ = this.gnpisService.getSource(sourceURI); @@ -67,6 +79,13 @@ export class StudyCardComponent implements OnInit { .subscribe(src => { this.studySource = src; }); + } else { + const urgiURI = 'https://urgi.versailles.inra.fr'; + const source$ = this.gnpisService.getSource(urgiURI); + source$ + .subscribe(src => { + this.studySource = src; + }); } }); @@ -74,14 +93,16 @@ export class StudyCardComponent implements OnInit { const variable$ = this.brapiService.studyObservationVariables(studyDbId).toPromise(); variable$ .then(response => { - this.studyObservationVariables = response.result.data; + this.studyObservationVariables = response.result.data.sort((var1, var2) => + var1.observationVariableDbId.localeCompare(var2.observationVariableDbId)); }); this.studyGermplasms = []; const germplasm$ = this.brapiService.studyGermplasms(studyDbId).toPromise(); germplasm$ .then(studyGermplasm => { - this.studyGermplasms = studyGermplasm.result.data; + this.studyGermplasms = studyGermplasm.result.data.sort((var1, var2) => + var1.germplasmName.localeCompare(var2.germplasmName)); }); this.loaded = Promise.all([study$, variable$, germplasm$]); @@ -92,10 +113,6 @@ export class StudyCardComponent implements OnInit { } - checkLocation(location: BrapiLocation) { - return location && location.longitude && location.latitude; - } - isNotURN(pui: string) { return !(pui.substring(0, 3) === 'urn'); } diff --git a/frontend/src/assets/gpds/logo.png b/frontend/src/assets/gpds/logo.png index 2131327a70020a31bbb43c56da919b9dccdb837c..16d4a77bc231332c1918cb684bcb199a8c0da555 100644 Binary files a/frontend/src/assets/gpds/logo.png and b/frontend/src/assets/gpds/logo.png differ diff --git a/frontend/src/assets/gpds/theme.scss b/frontend/src/assets/gpds/theme.scss index 286d878c78a754045b72b7e6aa8bae22f099448e..b4fae40f944d73954f77a92cb4855802124bcfc1 100644 --- a/frontend/src/assets/gpds/theme.scss +++ b/frontend/src/assets/gpds/theme.scss @@ -26,23 +26,18 @@ $enable-shadows: true; // public custom variables used in this theme, and in component styles - //custom tables .table { - border-bottom: 2px solid #0f6191; - border-top: 2px solid #0f6191; table-layout: fixed; -} + thead { + background-color: #0f6191; + } -a.btn.popovers { - text-decoration: underline; - //overflow: hidden; - //text-overflow: ellipsis; } .popover { - max-width: 50%; + max-width: 100%; } /*table th, table td { overflow: hidden; }*/ @@ -62,10 +57,6 @@ h4 { color: #0f6191; } -.table thead { - background-color: #0f6191; -} - .ellipsis { overflow: hidden; text-overflow: ellipsis; diff --git a/frontend/src/environments/environment.prod.ts b/frontend/src/environments/environment.prod.ts index c527bca54680f1cbcdc1a11c4966513caf951014..2b34b7fd446b963a2f5af1aaf9f47f555570498a 100644 --- a/frontend/src/environments/environment.prod.ts +++ b/frontend/src/environments/environment.prod.ts @@ -4,7 +4,6 @@ export const environment = { navbar: { title: 'GnpIS Plant Data Search', links: [ - { label: 'INRA', url: 'http://www.inra.fr/' }, { label: 'URGI', url: '#', @@ -14,52 +13,10 @@ export const environment = { { label: 'About us', url: 'https://urgi.versailles.inra.fr/About-us' } ] }, - { - label: 'Taxon/Germplasm', - url: '#', - subMenu: [ - { label: 'Taxon', url: 'https://urgi.versailles.inra.fr/siregal/common/taxon/form.do' }, - { label: 'Accession Simple', url: 'https://urgi.versailles.inra.fr/gnpis-core' }, - { label: 'Accession passport', url: 'https://urgi.versailles.inra.fr/siregal/siregal/accessionForm.do' }, - { label: 'Collections CRB', url: 'https://urgi.versailles.inra.fr/siregal/siregal/grc.do' }, - ] - }, - { label: 'Phenotyping', url: 'https://urgi.versailles.inra.fr/ephesis/ephesis/viewer.do' }, - { - label: 'Polymorphism', - url: '#', - subMenu: [ - { label: 'Genotyping', url: 'https://urgi.versailles.inra.fr/GnpSNP/snp/genotyping/form.do' }, - { label: 'SNP Discovery', url: 'https://urgi.versailles.inra.fr/GnpSNP/snp/welcome.do' }, - ] - }, - { label: 'Association', url: 'https://urgi.versailles.inra.fr/association/association/viewer.do#form' }, - { - label: 'Map/Marker/QTL', - url: '#', - subMenu: [ - { label: 'Map', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/searchMap.do' }, - { label: 'Loci', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/loci/queryLociSelect.do' }, - { label: 'QTL', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/qtl/queryQtlSelect.do' }, - { label: 'MetaQTLs', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/metaqtl/form.do' }, - { label: 'Marker', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/marker/markerForm.do' }, - { label: 'Pool', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/pool/poolForm.do' }, - { label: 'Traits', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/queryTraitSelect.do' }, - { label: 'Biomercator', url: 'https://urgi.versailles.inra.fr/Tools/BioMercator-V4' }, - ] - }, - { label: 'Genomes', url: 'https://urgi.versailles.inra.fr/Data/Genome/Genome-data-access' }, - { label: 'Synteny', url: 'https://urgi.versailles.inra.fr/synteny/synteny/viewer.do#dataset' }, - { - label: 'Sequence', - url: '#', - subMenu: [ - { label: 'Sequence', url: 'https://urgi.versailles.inra.fr/sequence/sequence/sequence/form.do' }, - { label: 'Experiment', url: 'https://urgi.versailles.inra.fr/sequence/sequence/experiment/form.do' }, - { label: 'Analysis', url: 'https://urgi.versailles.inra.fr/sequence/sequence/analysis/form.do' }, - { label: 'Project', url: 'https://urgi.versailles.inra.fr/sequence/sequence/project/form.do' }, - ] - } + { label: 'GNPIS', url: 'https://urgi.versailles.inra.fr/gnpis/' }, + { label: 'CIRAD', url: 'http://tropgenedb.cirad.fr/' }, + { label: 'VIB', url: 'http://pippa.psb.ugent.be' }, + { label: 'IBET', url: 'https://biodata.pt' } ] } diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 8789a59021563c54819f81169b262e9cc72bc61b..081a6813fd51e32e8e0d9bb72186227c57adeb3f 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -7,7 +7,6 @@ export const environment = { navbar: { title: 'GnpIS Plant Data Search', links: [ - { label: 'INRA', url: 'http://www.inra.fr/' }, { label: 'URGI', url: '#', @@ -17,52 +16,10 @@ export const environment = { { label: 'About us', url: 'https://urgi.versailles.inra.fr/About-us' } ] }, - { - label: 'Taxon/Germplasm', - url: '#', - subMenu: [ - { label: 'Taxon', url: 'https://urgi.versailles.inra.fr/siregal/common/taxon/form.do' }, - { label: 'Accession Simple', url: 'https://urgi.versailles.inra.fr/gnpis-core' }, - { label: 'Accession passport', url: 'https://urgi.versailles.inra.fr/siregal/siregal/accessionForm.do' }, - { label: 'Collections CRB', url: 'https://urgi.versailles.inra.fr/siregal/siregal/grc.do' }, - ] - }, - { label: 'Phenotyping', url: 'https://urgi.versailles.inra.fr/ephesis/ephesis/viewer.do' }, - { - label: 'Polymorphism', - url: '#', - subMenu: [ - { label: 'Genotyping', url: 'https://urgi.versailles.inra.fr/GnpSNP/snp/genotyping/form.do' }, - { label: 'SNP Discovery', url: 'https://urgi.versailles.inra.fr/GnpSNP/snp/welcome.do' }, - ] - }, - { label: 'Association', url: 'https://urgi.versailles.inra.fr/association/association/viewer.do#form' }, - { - label: 'Map/Marker/QTL', - url: '#', - subMenu: [ - { label: 'Map', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/searchMap.do' }, - { label: 'Loci', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/loci/queryLociSelect.do' }, - { label: 'QTL', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/qtl/queryQtlSelect.do' }, - { label: 'MetaQTLs', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/metaqtl/form.do' }, - { label: 'Marker', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/marker/markerForm.do' }, - { label: 'Pool', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/pool/poolForm.do' }, - { label: 'Traits', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/queryTraitSelect.do' }, - { label: 'Biomercator', url: 'https://urgi.versailles.inra.fr/Tools/BioMercator-V4' }, - ] - }, - { label: 'Genomes', url: 'https://urgi.versailles.inra.fr/Data/Genome/Genome-data-access' }, - { label: 'Synteny', url: 'https://urgi.versailles.inra.fr/synteny/synteny/viewer.do#dataset' }, - { - label: 'Sequence', - url: '#', - subMenu: [ - { label: 'Sequence', url: 'https://urgi.versailles.inra.fr/sequence/sequence/sequence/form.do' }, - { label: 'Experiment', url: 'https://urgi.versailles.inra.fr/sequence/sequence/experiment/form.do' }, - { label: 'Analysis', url: 'https://urgi.versailles.inra.fr/sequence/sequence/analysis/form.do' }, - { label: 'Project', url: 'https://urgi.versailles.inra.fr/sequence/sequence/project/form.do' }, - ] - } + { label: 'GNPIS', url: 'https://urgi.versailles.inra.fr/gnpis/' }, + { label: 'CIRAD', url: 'http://tropgenedb.cirad.fr/' }, + { label: 'VIB', url: 'http://pippa.psb.ugent.be' }, + { label: 'IBET', url: 'https://biodata.pt' } ] } diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index b7abb8b9a3a24e6a862fa5c1e9d3e4095ccebd11..e5b4643c3c14eaff971818c3d64e406d650b5482 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -10,6 +10,39 @@ $fa-font-path: '~font-awesome/fonts'; } } -a { - text-decoration: underline; +h3 { + font-weight: bold; + color: #0f6191; +} + +.card-section-body { + border: 2px solid rgb(195, 214, 226); + border-top-width: 0px; + border-bottom-left-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} + +.row-sep { + border-top: 1px solid #f0f0f0; +} + +.row-sep .row-sep:first-of-type { + border: none; +} + +.content-overflow { + max-height: 175px; + overflow-y: auto; +} + +.content-overflow-big { + max-height: 275px; + overflow: auto; +} + +.display-spinner-front { + position: absolute; + top: 70px; + left: 720px; + background-color: #F9F9F9; } diff --git a/frontend/src/tslint.json b/frontend/src/tslint.json index 9a8fed218220e47daab1f3733704ab4ad76f4373..7e545ab7d80894dd3456be1fee601237e43b7bff 100644 --- a/frontend/src/tslint.json +++ b/frontend/src/tslint.json @@ -15,7 +15,7 @@ ], "template-cyclomatic-complexity": [ true, - 260 + 10 ] } }