From 4c11f518533ad4c5a5ffb1f1686206f3657b326f Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 25 Jul 2019 12:26:39 +0200 Subject: [PATCH] feat: Add plantMaterial export button on germplasm card. GNP-5540. --- .../gnpis/v1/GnpISGermplasmController.java | 12 ++- frontend/package-lock.json | 88 +++++++++++++++---- frontend/package.json | 2 + .../germplasm-card.component.html | 2 + .../germplasm-card.component.ts | 22 ++++- frontend/src/app/gnpis.service.spec.ts | 42 ++++++++- frontend/src/app/gnpis.service.ts | 22 ++++- frontend/src/app/models/gnpis.model.ts | 9 ++ 8 files changed, 169 insertions(+), 30 deletions(-) diff --git a/backend/src/main/java/fr/inra/urgi/faidare/api/gnpis/v1/GnpISGermplasmController.java b/backend/src/main/java/fr/inra/urgi/faidare/api/gnpis/v1/GnpISGermplasmController.java index 5b32adbb..210b50fe 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/api/gnpis/v1/GnpISGermplasmController.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/api/gnpis/v1/GnpISGermplasmController.java @@ -12,16 +12,14 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; import java.io.File; import java.util.Collections; -import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(tags = {"GnpIS API"}, description = "Extended GnpIS API") @RestController @@ -89,8 +87,8 @@ public class GnpISGermplasmController { * resp.setContentType("application/zip"); * </pre> */ - @RequestMapping(value = "/csv", method = GET, produces = "text/csv") - public FileSystemResource export(GermplasmPOSTSearchCriteria criteria, HttpServletResponse response) { + @PostMapping(value = "/csv", produces = "text/csv", consumes = APPLICATION_JSON_VALUE) + public FileSystemResource export(@RequestBody @Valid GermplasmPOSTSearchCriteria criteria, HttpServletResponse response) { try { File exportFile = germplasmService.exportCSV(criteria); response.setHeader("Content-Disposition", "attachment; filename=germplasm.gnpis.csv"); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aa2aa4ef..f3d2d502 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -821,6 +821,12 @@ } } }, + "@types/file-saver": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.1.tgz", + "integrity": "sha512-g1QUuhYVVAamfCifK7oB7G3aIl4BbOyzDOqVyUfEr4tfBKrXfeH+M+Tg7HKCXSrbzxYdhyCP7z9WbKo0R2hBCw==", + "dev": true + }, "@types/geojson": { "version": "7946.0.6", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.6.tgz", @@ -1273,6 +1279,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" @@ -2484,7 +2491,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", @@ -2886,7 +2894,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", @@ -3608,6 +3617,11 @@ "schema-utils": "^1.0.0" } }, + "file-saver": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" + }, "fileset": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", @@ -3853,7 +3867,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3874,12 +3889,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" @@ -3894,17 +3911,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", @@ -4021,7 +4041,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4033,6 +4054,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4047,6 +4069,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4054,12 +4077,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" @@ -4078,6 +4103,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4158,7 +4184,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4170,6 +4197,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4255,7 +4283,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4291,6 +4320,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", @@ -4310,6 +4340,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4353,12 +4384,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 } } }, @@ -4367,6 +4400,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", @@ -4379,6 +4413,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", @@ -4416,7 +4451,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", @@ -4605,7 +4641,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", @@ -5325,7 +5362,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", @@ -5968,6 +6006,7 @@ "resolved": "http://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", @@ -5980,7 +6019,8 @@ "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -6255,7 +6295,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", @@ -6919,6 +6960,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", @@ -7933,6 +7975,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", @@ -7944,6 +7987,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", @@ -7954,7 +7998,8 @@ "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -7963,6 +8008,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" @@ -7973,6 +8019,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" @@ -7983,6 +8030,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" } @@ -9263,6 +9311,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" } @@ -10625,6 +10674,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 e4dc6e1e..d89b3596 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "angular-mocks": "1.7.8", "bootstrap": "4.1.3", "core-js": "2.5.7", + "file-saver": "2.0.2", "font-awesome": "4.7.0", "leaflet": "1.3.4", "leaflet.markercluster": "1.4.1", @@ -46,6 +47,7 @@ "@angular/cli": "7.3.4", "@angular/compiler-cli": "7.2.7", "@angular/language-service": "7.2.7", + "@types/file-saver": "2.0.1", "@types/jasmine": "2.8.12", "@types/jasminewd2": "2.0.6", "@types/node": "8.9.5", diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.html b/frontend/src/app/germplasm-card/germplasm-card.component.html index 7d8b923b..4381eb02 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.html +++ b/frontend/src/app/germplasm-card/germplasm-card.component.html @@ -13,6 +13,8 @@ <div class="row align-items-center justify-content-center"> + <button (click)="exportPlantMaterial(germplasmGnpis)">Export</button> + <!--Templates for gerplasm card--> <ng-template #taxonTemplate> diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.ts b/frontend/src/app/germplasm-card/germplasm-card.component.ts index 73616b70..842cf858 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.ts +++ b/frontend/src/app/germplasm-card/germplasm-card.component.ts @@ -3,8 +3,9 @@ import { ActivatedRoute } from '@angular/router'; import { BrapiService } from '../brapi.service'; import { GnpisService } from '../gnpis.service'; import { BrapiAttributeData, BrapiGermplasmPedigree, BrapiLocation, BrapiTaxonIds } from '../models/brapi.model'; -import { Children, Germplasm, Site } from '../models/gnpis.model'; +import { Children, Germplasm, GermplasmExportCriteria, Site } from '../models/gnpis.model'; import { environment } from '../../environments/environment'; +import { saveAs } from 'file-saver'; @Component({ selector: 'faidare-germplasm-card', @@ -231,4 +232,23 @@ export class GermplasmCardComponent implements OnInit { } return 0; } + + exportPlantMaterial(germplasm: Germplasm) { + const germplasmExportCriteria: GermplasmExportCriteria = { + accessionNumbers: [germplasm.accessionNumber], + germplasmDbIds: [], + germplasmGenus: [], + germplasmNames: [], + germplasmPUIs: [germplasm.germplasmPUI], + germplasmSpecies: [] + }; + this.gnpisService.plantMaterialExport(germplasmExportCriteria).subscribe( + result => { + const blob = new Blob([result], { type: 'text/plain;charset=utf-8' }); + saveAs(blob, 'germplasm.gnpis.csv'); + }, + error => { + console.log(error); + }); + } } diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index 60327439..1213783d 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -3,7 +3,15 @@ import { BrapiMetaData, BrapiResults } from './models/brapi.model'; import { DataDiscoveryCriteria, DataDiscoverySource } from './models/data-discovery.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'; +import { + Donor, + Germplasm, + GermplasmExportCriteria, + GermplasmInstitute, + GermplasmSet, + Institute, + Site +} from './models/gnpis.model'; describe('GnpisService', () => { @@ -115,6 +123,25 @@ describe('GnpisService', () => { population: [germplasmSet] }; + const germplasmExportCriteria: GermplasmExportCriteria = { + accessionNumbers: ['VCR010'], + germplasmDbIds: [], + germplasmGenus: [], + germplasmNames: [], + germplasmPUIs: [], + germplasmSpecies: [] + }; + + const exportFile: string = '"DOI";"AccessionNumber";' + + '"AccessionName";"TaxonGroup";' + + '"HoldingInstitution";"LotName";' + + '"LotSynonym";' + + '"CollectionName";' + + '"CollectionType";' + + '"PanelName";' + + '"PanelSize"\n' + + '"https://germplasmdoi";"germplasm01";GermplasmTest;Pea;INRA-URGI;;;;;;'; + let gnpisService: GnpisService; let http: HttpTestingController; beforeEach(() => { @@ -228,5 +255,18 @@ describe('GnpisService', () => { }); }); }); + + it('should export germplasm as PlantMaterial', () => { + gnpisService.plantMaterialExport(germplasmExportCriteria).subscribe( + plantMaterialExport => { + expect(plantMaterialExport).toEqual(exportFile); + } + ); + const req = http.expectOne({ + url: `${BASE_URL}/germplasm/csv`, + method: 'POST' + }); + req.flush(exportFile); + }); }) ; diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index 0e91070d..4a3f30a2 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -1,10 +1,15 @@ import { Injectable } from '@angular/core'; import { Observable, ReplaySubject, zip } from 'rxjs'; import { HttpClient } from '@angular/common/http'; -import { DataDiscoveryCriteria, DataDiscoveryFacet, DataDiscoveryResults, DataDiscoverySource } from './models/data-discovery.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 { Germplasm, GermplasmExportCriteria } from './models/gnpis.model'; import { XrefResponse } from './models/xref.model'; import { removeNullUndefined } from './utils'; @@ -118,4 +123,17 @@ export class GnpisService { return this.http.get<XrefResponse>(`${BASE_URL}/xref/documentbyfulltextid?linkedRessourcesID=${xrefId}`); } + // TODO Change return type + plantMaterialExport(criteria: GermplasmExportCriteria): Observable<any> { + const requestOptions: Object = { + /* other options here */ + responseType: 'text' + }; + return this.http.post<any>( + `${BASE_URL}/germplasm/csv`, + criteria, + requestOptions + ); + } + } diff --git a/frontend/src/app/models/gnpis.model.ts b/frontend/src/app/models/gnpis.model.ts index edee6ddf..5a8e4a6d 100644 --- a/frontend/src/app/models/gnpis.model.ts +++ b/frontend/src/app/models/gnpis.model.ts @@ -1,5 +1,14 @@ import { BrapiDonor, BrapiGermplasm } from './brapi.model'; +export interface GermplasmExportCriteria { + accessionNumbers: string[]; + germplasmDbIds: string[]; + germplasmGenus: string[]; + germplasmNames: string[]; + germplasmPUIs: string[]; + germplasmSpecies: string[]; +} + export interface Germplasm extends BrapiGermplasm { genusSpecies: string; genusSpeciesSubtaxa: string; -- GitLab