Commit f0f1143a authored by Jérémy Destin's avatar Jérémy Destin
Browse files

Merge branch 'feat/implement_germplasm_result_page' into 'master'

Feat/implement germplasm result page

Closes GNP-4309

See merge request !46
parents 69ab8609 bc271570
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Gradle Imported" enabled="true">
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false">
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-configuration-processor/2.1.2.RELEASE/db9671c321defb942a6700fae8a7700a137a25e/spring-boot-configuration-processor-2.1.2.RELEASE.jar" />
</processorPath>
<module name="faidare.backend.main" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="faidare.backend.main" target="1.8" />
<module name="faidare.backend.test" target="1.8" />
......
......@@ -3,25 +3,25 @@ package fr.inra.urgi.faidare.api.faidare.v1;
import com.google.common.base.Strings;
import fr.inra.urgi.faidare.api.BadRequestException;
import fr.inra.urgi.faidare.api.NotFoundException;
import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria;
import fr.inra.urgi.faidare.domain.criteria.GermplasmGETSearchCriteria;
import fr.inra.urgi.faidare.domain.criteria.GermplasmPOSTSearchCriteria;
import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO;
import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse;
import fr.inra.urgi.faidare.domain.response.PaginatedList;
import fr.inra.urgi.faidare.service.es.GermplasmService;
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 = {"FAIDARE API"}, description = "Extended FAIDARE API")
@RestController
......@@ -89,8 +89,9 @@ 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");
......@@ -101,4 +102,27 @@ public class GnpISGermplasmController {
}
}
@PostMapping(value = "/germplasm-list-csv", produces = "text/csv", consumes = APPLICATION_JSON_VALUE)
public FileSystemResource export(@RequestBody @Valid FaidareGermplasmPOSTShearchCriteria criteria, HttpServletResponse response) {
try {
File exportFile = germplasmService.exportListGermplasmCSV(criteria);
response.setHeader("Content-Disposition", "attachment; filename=germplasm.gnpis.csv");
return new FileSystemResource(exportFile);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("An error occurred when exporting germplasm: " + e.getMessage() + ".", e);
}
}
@ApiOperation("Search list of germplasm")
@PostMapping(value = "/search", consumes = APPLICATION_JSON_VALUE)
public GermplasmSearchResponse germplasmSearch(@RequestBody @Valid FaidareGermplasmPOSTShearchCriteria criteria) {
try {
return germplasmService.germplasmFind(criteria);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
package fr.inra.urgi.faidare.domain.criteria;
import fr.inra.urgi.faidare.domain.criteria.base.SortCriteria;
import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO;
import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.CriteriaForDocument;
import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.DocumentPath;
import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.NoDocumentMapping;
import java.util.List;
/**
* @author jdestin
*/
@CriteriaForDocument(GermplasmVO.class)
public class FaidareGermplasmPOSTShearchCriteria extends GermplasmPOSTSearchCriteria implements SortCriteria {
@DocumentPath(value = "synonyms")
private List<String> synonyms;
/*@DocumentPath(value = {"panel", "name"}, objectsValue = CollPopVO.class)
private List<String> panel;
@DocumentPath(value = {"collection", "name"}, objectsValue = CollPopVO.class)
private List<String> collection;
@DocumentPath(value = {"population", "name"}, objectsValue = CollPopVO.class)
private List<String> population;*/
@DocumentPath(value = "commonCropName")
private List<String> commonCropName;
@DocumentPath(value = "species")
private List<String> species;
@DocumentPath(value = "genus")
private List<String> genus;
@DocumentPath(value = "genusSpecies")
private List<String> genusSpecies;
@DocumentPath(value = "subtaxa")
private List<String> subtaxa;
@DocumentPath(value = "genusSpeciesSubtaxa")
private List<String> genusSpeciesSubtaxa;
@DocumentPath("taxonSynonyms")
private List<String> taxonSynonyms;
@DocumentPath("taxonCommonNames")
private List<String> taxonCommonNames;
@DocumentPath(value = {"holdingInstitute", "organisation"})
private List<String> holdingInstitute;
@DocumentPath("sourceUri")
private List<String> sources;
@DocumentPath("biologicalStatusOfAccessionCode")
private List<String> biologicalStatus;
@DocumentPath("geneticNature")
private List<String> geneticNature;
@DocumentPath("countryOfOriginCode")
private List<String> country;
@NoDocumentMapping
private List<String> facetFields;
@NoDocumentMapping
private String sortBy = null;// = "schema:name";
@NoDocumentMapping
private String sortOrder = null;// = SortOrder.ASC.name();
/*public List<String> getPanel() { return panel; }
public void setPanel(List<String> panel) { this.panel = panel; }
public List<String> getCollection() { return collection; }
public void setCollection(List<String> collection) { this.collection = collection; }
public List<String> getPopulation() { return population; }
public void setPopulation(List<String> population) { this.population = population; }*/
public List<String> getCommonCropName() { return commonCropName; }
public void setCommonCropName(List<String> commonCropName) {
this.commonCropName = commonCropName;
}
public List<String> getSpecies() { return species; }
public void setSpecies(List<String> species) { this.species = species; }
public List<String> getGenus() { return genus; }
public void setGenus(List<String> genus) { this.genus = genus; }
public List<String> getGenusSpecies() { return genusSpecies; }
public void setGenusSpecies(List<String> genusSpecies) {
this.genusSpecies = genusSpecies;
}
public List<String> getSubtaxa() { return subtaxa; }
public void setSubtaxa(List<String> subtaxa) { this.subtaxa = subtaxa; }
public List<String> getGenusSpeciesSubtaxa() { return genusSpeciesSubtaxa; }
public void setGenusSpeciesSubtaxa(List<String> genusSpeciesSubtaxa) {
this.genusSpeciesSubtaxa = genusSpeciesSubtaxa;
}
public List<String> getTaxonSynonyms() { return taxonSynonyms; }
public void setTaxonSynonyms(List<String> taxonSynonyms) {
this.taxonSynonyms = taxonSynonyms;
}
public List<String> getSynonyms() { return synonyms; }
public void setSynonyms(List<String> synonyms) { this.synonyms = synonyms; }
public List<String> getTaxonCommonNames() {
return taxonCommonNames;
}
public void setTaxonCommonNames(List<String> taxonCommonNames) {
this.taxonCommonNames = taxonCommonNames;
}
public List<String> getHoldingInstitute() {
return holdingInstitute;
}
public void setHoldingInstitute(List<String> holdingInstitute) {
this.holdingInstitute = holdingInstitute;
}
public List<String> getSources() {
return sources;
}
public void setSources(List<String> sources) {
this.sources = sources;
}
public List<String> getBiologicalStatus() { return biologicalStatus; }
public void setBiologicalStatus(List<String> biologicalStatus) {
this.biologicalStatus = biologicalStatus;
}
public List<String> getGeneticNature() {
return geneticNature;
}
public void setGeneticNature(List<String> geneticNature) {
this.geneticNature = geneticNature;
}
public List<String> getCountry() { return country; }
public void setCountry(List<String> country) {
this.country = country;
}
public void setSortBy(String sortBy) {
this.sortBy = sortBy;
}
public void setSortOrder(String sortOrder) {
this.sortOrder = sortOrder;
}
@Override
public String getSortBy() {
return sortBy;
}
@Override
public String getSortOrder() {
return sortOrder;
}
public List<String> getFacetFields() {
return facetFields;
}
public void setFacetFields(List<String> facetFields) {
this.facetFields = facetFields;
}
}
......@@ -23,6 +23,12 @@ public interface ExtendedGermplasm extends BrapiGermplasm, GnpISInternal {
@JsonView(JSONView.GnpISFields.class)
String getTaxonComment();
@JsonView(JSONView.GnpISFields.class)
public String getGenusSpecies();
@JsonView(JSONView.GnpISFields.class)
public String getGenusSpeciesSubtaxa();
@JsonView(JSONView.GnpISFields.class)
String getGeneticNature();
......
......@@ -48,8 +48,10 @@ public class GermplasmVO
private String genus;
private String species;
private String genusSpecies;
private String speciesAuthority;
private String subtaxa;
private String genusSpeciesSubtaxa;
private String subtaxaAuthority;
private String acquisitionDate;
......@@ -292,6 +294,13 @@ public class GermplasmVO
this.species = species;
}
@Override
public String getGenusSpecies() { return genusSpecies; }
public void setGenusSpecies(String genusSpecies) {
this.genusSpecies = genusSpecies;
}
@Override
public String getSpeciesAuthority() {
return speciesAuthority;
......@@ -310,6 +319,15 @@ public class GermplasmVO
this.subtaxa = subtaxa;
}
@Override
public String getGenusSpeciesSubtaxa() {
return genusSpeciesSubtaxa;
}
public void setGenusSpeciesSubtaxa(String genusSpeciesSubtaxa) {
this.genusSpeciesSubtaxa = genusSpeciesSubtaxa;
}
@Override
public String getSubtaxaAuthority() {
return subtaxaAuthority;
......
package fr.inra.urgi.faidare.domain.datadiscovery.response;
import com.fasterxml.jackson.annotation.JsonView;
import fr.inra.urgi.faidare.domain.JSONView;
import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiData;
import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiListResponse;
import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiMetadata;
import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO;
import fr.inra.urgi.faidare.domain.datadiscovery.data.Facet;
import java.util.List;
public interface GermplasmSearchResponse extends BrapiListResponse<GermplasmVO> {
@Override
@JsonView(JSONView.GnpISFields.class)
BrapiMetadata getMetadata();
@Override
@JsonView(JSONView.GnpISFields.class)
BrapiData<GermplasmVO> getResult();
@JsonView(JSONView.GnpISFields.class)
List<? extends Facet> getFacets();
}
......@@ -2,9 +2,11 @@ package fr.inra.urgi.faidare.domain.response;
import fr.inra.urgi.faidare.api.brapi.v1.exception.BrapiPaginationException;
import fr.inra.urgi.faidare.domain.brapi.v1.response.*;
import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO;
import fr.inra.urgi.faidare.domain.datadiscovery.data.DataDiscoveryDocument;
import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl;
import fr.inra.urgi.faidare.domain.datadiscovery.response.DataDiscoveryResponse;
import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse;
import org.springframework.http.HttpStatus;
import java.util.ArrayList;
......@@ -114,4 +116,12 @@ public class ApiResponseFactory {
BrapiMetadata metadata = ApiResponseFactory.createMetadata(pagination, null);
return new DataDiscoveryResponseImpl(metadata, results, facets);
}
/**
* Create germplasm search response (brapi list response with facets)
*/
public static GermplasmSearchResponse createGermplasmListResponseWithFacets(Pagination pagination, List<GermplasmVO> results, List<FacetImpl> facets) {
BrapiMetadata metadata = ApiResponseFactory.createMetadata(pagination, null);
return new GermplasmSearchResponseImpl(metadata, results, facets);
}
}
package fr.inra.urgi.faidare.domain.response;
import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiMetadata;
import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO;
import fr.inra.urgi.faidare.domain.datadiscovery.data.Facet;
import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl;
import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse;
import java.util.List;
public class GermplasmSearchResponseImpl extends ApiListResponseImpl<GermplasmVO> implements GermplasmSearchResponse {
private final List<FacetImpl> facets;
public GermplasmSearchResponseImpl(BrapiMetadata metadata, List<GermplasmVO> result, List<FacetImpl> facets) {
super(metadata, result);
this.facets = facets;
}
@Override
public List<? extends Facet> getFacets() {
return facets;
}
}
......@@ -12,4 +12,6 @@ public interface ESQueryFactory<C> {
*/
QueryBuilder createQuery(C criteria);
QueryBuilder createShouldFilterQuery(C criteria);
}
......@@ -22,7 +22,7 @@ import static org.elasticsearch.index.query.QueryBuilders.*;
* Generic Elasticsearch query generator for criteria mapped on value object via
* {@link DocumentPath} annotations
*
* @author gcornut
* @author gcornut, jdestin
*/
public class ESGenericQueryFactory<C> implements ESQueryFactory<C> {
......@@ -55,6 +55,24 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> {
}
}
@Override
public QueryBuilder createShouldFilterQuery(C criteria) {
try {
CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass());
DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata();
List<QueryBuilder> queries = createQueryFromMapping(criteria, null, null, voMappingToCriteria, documentMetadata);
if (!queries.isEmpty()) {
return germplasmFilterQueries(queries);
} else {
return matchAllQuery();
}
} catch (Exception e) {
throw new ESQueryGenerationException(e);
}
}
/**
* Same as {@link ESGenericQueryFactory#createQuery(Object)} but with a list of document fields to exclude from query
*/
......@@ -76,6 +94,24 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> {
}
}
public QueryBuilder createEsShouldQueryExcludingFields(C criteria, String... excludeDocumentFields) {
try {
CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass());
DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata();
Set<String> excludedDocumentFields = ImmutableSet.copyOf(excludeDocumentFields);
List<QueryBuilder> queries = createQueryFromMapping(criteria, null, excludedDocumentFields, voMappingToCriteria, documentMetadata);
if (!queries.isEmpty()) {
return germplasmFilterQueries(queries);
} else {
return matchAllQuery();
}
} catch (Exception e) {
throw new ESQueryGenerationException(e);
}
}
/**
* Same as {@link ESGenericQueryFactory#createQuery(Object)} but with a list of document fields to include from query (excluding all others)
*/
......@@ -97,6 +133,24 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> {
}
}
public QueryBuilder createEsShouldQueryIncludingFields(C criteria, String... includeDocumentFields) {
try {
CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass());
DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata();
Set<String> includedDocumentFields = ImmutableSet.copyOf(includeDocumentFields);
List<QueryBuilder> queries = createQueryFromMapping(criteria, includedDocumentFields, null, voMappingToCriteria, documentMetadata);
if (!queries.isEmpty()) {
return germplasmFilterQueries(queries);
} else {
return matchAllQuery();
}
} catch (Exception e) {
throw new ESQueryGenerationException(e);
}
}
/**
* Generate a query from a criteria
*/
......@@ -287,7 +341,7 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> {
/**
* Combine queries into a bool must query
*
* @return null if not queries given; the first query if only only query is given; a bool query otherwise
* @return null if not queries given; the first query if only one query is given; a bool query otherwise
*/
public static QueryBuilder andQueries(List<QueryBuilder> queries) {
if (queries == null || queries.isEmpty()) {
......@@ -306,4 +360,52 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> {
public static QueryBuilder andQueries(QueryBuilder... queries) {
return andQueries(Arrays.asList(queries));
}
/**
* Combine queries into a bool should query
*
* @return null if not queries given; the first query if only one query is given; a bool query otherwise
*/
public static QueryBuilder germplasmFilterQueries(List<QueryBuilder> queries) {
if (queries == null || queries.isEmpty()) {
return null;
} else if (queries.size() == 1) {
return queries.get(0);
} else {
// List of the criteria that will be use in should query part, the other criteria will be used as filter.
List<String> shouldCriterion = Arrays.asList("commonCropName",
"species", "germplasmGenus", "genusSpecies", "subtaxa", "genus",
"genusSpeciesSubtaxa", "taxonSynonyms", "taxonCommonNames", "panel", "collection", "population",
"germplasmName", "accessionNumber", "synonyms");
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
BoolQueryBuilder boolShouldQueryBuilder = QueryBuilders.boolQuery();
List<QueryBuilder> filterQueries = new ArrayList<>();
for (QueryBuilder query : queries) {
boolean isShouldCriterion = stringContainsItemFromList(query.toString(), shouldCriterion);
if (isShouldCriterion){
boolShouldQueryBuilder.should(query);
} else {
filterQueries.add(query);
}
}
boolQueryBuilder.must(boolShouldQueryBuilder);
for (QueryBuilder query: filterQueries) {
boolQueryBuilder.filter(query);
}
return boolQueryBuilder;
}
}
public static boolean stringContainsItemFromList