Commit d0401c8e authored by Maxym Borodenko's avatar Maxym Borodenko
Browse files

Merge branch '537-taxonomic-backbone' into 'master'

Resolve "Taxonomic backbone"

Closes #537

See merge request genesys-pgr/genesys-server!568
parents ad4341f9 2c685b04
......@@ -701,6 +701,23 @@
<artifactId>hashids</artifactId>
<version>1.0.3</version>
</dependency>
<!-- GRIN Taxonomy -->
<dependency>
<groupId>org.genesys-pgr</groupId>
<artifactId>grin-taxonomy-downloader</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.genesys-pgr</groupId>
<artifactId>grin-taxonomy-reader</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.genesys-pgr</groupId>
<artifactId>taxonomychecker</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
......
......@@ -21,16 +21,11 @@ import java.util.List;
import org.genesys2.server.api.ApiBaseController;
import org.genesys2.server.api.ModelValidationException;
import org.genesys2.server.exception.AuthorizationException;
import org.genesys2.server.exception.NotFoundElement;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
import org.genesys2.server.model.impl.CropTaxonomy;
import org.genesys2.server.service.CropService;
import org.genesys2.server.service.GenesysService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -74,10 +69,8 @@ public class CropsController extends ApiBaseController {
/**
* Add a crop
* @return
*
* @return
* @throws ValidationException
*/
@RequestMapping(value = { "/save" }, method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public Crop saveCrop(@RequestBody Crop cropJson) {
......@@ -124,82 +117,4 @@ public class CropsController extends ApiBaseController {
LOG.info("Getting crop {}", shortName);
return cropService.delete(cropService.getCrop(shortName));
}
/**
* Get crop taxonomy rules /crops/{shortName}/rules
* @return
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/{shortName}/rules", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public List<CropRule> getCropRules(@PathVariable("shortName") String shortName) throws AuthorizationException {
LOG.info("Getting crop rules {}", shortName);
final Crop crop = cropService.getCrop(shortName);
final List<CropRule> cropRules = cropService.getCropRules(crop);
return cropRules;
}
/**
* Get crop taxonomy rules /crops/{shortName}/rules
*
* @return
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/{shortName}/rules", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody List<CropRule> updateCropRules(@PathVariable("shortName") String shortName, @RequestBody List<CropRule> rules) throws AuthorizationException {
LOG.info("Updating crop rules for {}", shortName);
final Crop crop = cropService.getCrop(shortName);
if (crop == null)
throw new NotFoundElement("No crop " + shortName);
cropService.setCropRules(crop, rules);
return cropService.getCropRules(crop);
}
/**
* Get crop taxonomies /crops/{shortName}/taxa
* @return
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/{shortName}/taxa", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public Page<CropTaxonomy> getCropTaxa(@PathVariable("shortName") String shortName) throws AuthorizationException {
LOG.info("Getting crop taxa {}", shortName);
final Crop crop = cropService.getCrop(shortName);
final Page<CropTaxonomy> cropTaxa = cropService.getCropTaxonomies(crop, PageRequest.of(0, 50));
return cropTaxa;
}
/**
* Rebuild taxonomy-crop lists
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "/rebuild", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public void rebuild() {
cropService.rebuildTaxonomies();
}
/**
* Rebuild taxonomy-crop lists
*
* @return
* @throws AuthorizationException
*/
@RequestMapping(value = "{shortName}/rebuild", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public void rebuildCrop(@PathVariable("shortName") String shortName) {
LOG.info("Updating crop rules for {}", shortName);
final Crop crop = cropService.getCrop(shortName);
if (crop == null)
throw new NotFoundElement("No crop " + shortName);
cropService.rebuildTaxonomies(crop);
}
}
......@@ -28,16 +28,12 @@ import org.genesys2.server.api.ModelValidationException;
import org.genesys2.server.exception.NotFoundElement;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
import org.genesys2.server.model.impl.CropTaxonomy;
import org.genesys2.server.service.CRMException;
import org.genesys2.server.service.CropService;
import org.genesys2.server.service.CropService.CropDetails;
import org.genesys2.spring.CSVMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.access.prepost.PreAuthorize;
......@@ -207,76 +203,6 @@ public class CropsController extends ApiBaseController {
return cropService.delete(cropService.getCrop(shortName));
}
/**
* Get crop taxonomy rules /crops/{shortName}/rules
*
* @return
*/
@RequestMapping(value = "/{shortName}/rules", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public List<CropRule> getCropRules(@PathVariable("shortName") String shortName) {
LOG.info("Getting crop rules {}", shortName);
final Crop crop = cropService.getCrop(shortName);
final List<CropRule> cropRules = cropService.getCropRules(crop);
return cropRules;
}
/**
* Get crop taxonomy rules /crops/{shortName}/rules
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "/{shortName}/rules", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public List<CropRule> updateCropRules(@PathVariable("shortName") String shortName, @RequestBody List<CropRule> rules) {
LOG.info("Updating crop rules for {}", shortName);
final Crop crop = cropService.getCrop(shortName);
if (crop == null)
throw new NotFoundElement("No crop " + shortName);
cropService.setCropRules(crop, rules);
return cropService.getCropRules(crop);
}
/**
* Get crop taxonomies /crops/{shortName}/taxa
*
* @return
*/
@RequestMapping(value = "/{shortName}/taxa", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public Page<CropTaxonomy> getCropTaxa(@PathVariable("shortName") String shortName) {
LOG.info("Getting crop taxa {}", shortName);
final Crop crop = cropService.getCrop(shortName);
return cropService.getCropTaxonomies(crop, PageRequest.of(0, 50));
}
/**
* Rebuild taxonomy-crop lists
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "/rebuild", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public void rebuild() {
cropService.rebuildTaxonomies();
}
/**
* Rebuild taxonomy-crop lists
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "{shortName}/rebuild", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public void rebuildCrop(@PathVariable("shortName") String shortName) {
LOG.info("Updating crop rules for {}", shortName);
final Crop crop = cropService.getCrop(shortName);
if (crop == null)
throw new NotFoundElement("No crop " + shortName);
cropService.rebuildTaxonomies(crop);
}
/**
* Link accession#cropName with crop
*
......
......@@ -27,16 +27,12 @@ import java.util.UUID;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.opencsv.CSVReader;
import io.swagger.annotations.Api;
import org.genesys.blocks.model.JsonViews;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.taxonomy.gringlobal.component.CabReader;
import org.genesys2.server.api.ApiBaseController;
import org.genesys2.server.api.FilteredPage;
import org.genesys2.server.api.Pagination;
......@@ -51,7 +47,6 @@ import org.genesys2.server.service.DiversityTreeService.DiversityTreeSuggestionP
import org.genesys2.server.service.ElasticsearchService;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.filter.DiversityTreeFilter;
import org.genesys2.server.service.impl.CabReader;
import org.genesys2.server.service.impl.SearchException;
import org.genesys2.spring.CSVMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -74,6 +69,13 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.opencsv.CSVReader;
import io.swagger.annotations.Api;
/**
* The Class DiversityTreeController.
*
......
......@@ -16,9 +16,8 @@
package org.genesys2.server.api.v1;
import static com.google.common.collect.Sets.newHashSet;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
import static com.google.common.collect.Sets.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import java.util.Arrays;
import java.util.HashMap;
......@@ -26,11 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonView;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.genesys.blocks.model.BasicModel;
import org.genesys.blocks.model.EmptyModel;
import org.genesys.blocks.model.JsonViews;
import org.genesys.catalog.model.Partner;
import org.genesys.catalog.model.dataset.Dataset;
......@@ -50,6 +48,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.annotation.JsonView;
/**
* API to search the Catalog.
*
......@@ -85,8 +85,8 @@ public class SearchController extends ApiBaseController {
QueryBuilder extraFilters = filtersFromDataset(filters);
Set<Class<? extends BasicModel>> clazzes = newHashSet(Crop.class, Partner.class, Descriptor.class);
Map<Class<? extends BasicModel>, List<? extends BasicModel>> hitsByEntity = elasticsearch.search(extraFilters, searchQuery, clazzes);
Set<Class<? extends EmptyModel>> clazzes = newHashSet(Crop.class, Partner.class, Descriptor.class);
Map<Class<? extends EmptyModel>, List<? extends EmptyModel>> hitsByEntity = elasticsearch.search(extraFilters, searchQuery, clazzes);
Map<String, SearchResults<?>> suggestions = new HashMap<>();
suggestions.put("search.group.crop", SearchResults.from("code", Arrays.asList("crop"), hitsByEntity.get(Crop.class)));
......
......@@ -20,23 +20,20 @@ import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.annotations.Api;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import org.genesys.blocks.model.JsonViews;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys2.server.exception.DetailedConstraintViolationException;
import org.genesys2.server.exception.NotFoundElement;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
import org.genesys2.server.model.impl.CropTaxonomy;
import org.genesys2.server.service.CRMException;
import org.genesys2.server.service.CropService;
import org.genesys2.server.service.impl.SearchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.access.prepost.PreAuthorize;
......@@ -48,8 +45,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.annotations.Api;
@RestController("cropApi2")
@RequestMapping(value = { CropsController.CONTROLLER_URL})
......@@ -172,74 +170,6 @@ public class CropsController extends ApiBaseController {
}).collect(Collectors.toList());
}
/**
* Get crop taxonomy rules /crops/{shortName}/rules
*
* @return
*/
@RequestMapping(value = "/{shortName}/rules", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public List<CropRule> getCropRules(@PathVariable("shortName") String shortName) {
LOG.info("Getting crop rules {}", shortName);
final Crop crop = cropService.getCrop(shortName);
return cropService.getCropRules(crop);
}
/**
* Update crop taxonomy rules /crops/{shortName}/rules
*
* @return operation response
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "/{shortName}/rules", method = { RequestMethod.PUT, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public List<CropRule> updateCropRules(@PathVariable("shortName") String shortName, @RequestBody List<CropRule> rules) {
LOG.info("Updating crop rules for {}", shortName);
final Crop crop = cropService.getCrop(shortName);
if (crop == null) {
throw new NotFoundElement("Crop not found by shortName=" + shortName);
}
cropService.setCropRules(crop, rules);
return cropService.getCropRules(crop);
}
/**
* Get crop taxonomies /crops/{shortName}/taxa
*
* @return
*/
@RequestMapping(value = "/{shortName}/taxa", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public Page<CropTaxonomy> getCropTaxa(@PathVariable("shortName") String shortName) {
LOG.info("Getting crop taxa {}", shortName);
final Crop crop = cropService.getCrop(shortName);
return cropService.getCropTaxonomies(crop, PageRequest.of(0, 50));
}
/**
* Rebuild taxonomy-crop lists
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "/rebuild", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public void rebuild() {
cropService.rebuildTaxonomies();
}
/**
* Rebuild taxonomy-crop lists
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "{shortName}/rebuild", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public void rebuildCrop(@PathVariable("shortName") String shortName) {
LOG.info("Updating crop rules for {}", shortName);
final Crop crop = cropService.getCrop(shortName);
if (crop == null)
throw new NotFoundElement("No crop " + shortName);
cropService.rebuildTaxonomies(crop);
}
/**
* Link accession#cropName with crop
*
......
......@@ -16,8 +16,6 @@
package org.genesys2.server.api.v2;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
......@@ -28,18 +26,16 @@ import java.util.UUID;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.Validator;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.opencsv.CSVReader;
import io.swagger.annotations.Api;
import org.genesys.blocks.model.JsonViews;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.taxonomy.gringlobal.component.CabReader;
import org.genesys2.server.api.FilteredPage;
import org.genesys2.server.api.Pagination;
import org.genesys2.server.exception.DetailedConstraintViolationException;
......@@ -53,7 +49,6 @@ import org.genesys2.server.service.DiversityTreeService;
import org.genesys2.server.service.ElasticsearchService;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.filter.DiversityTreeFilter;
import org.genesys2.server.service.impl.CabReader;
import org.genesys2.server.service.impl.SearchException;
import org.genesys2.spring.CSVMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -74,6 +69,13 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.opencsv.CSVReader;
import io.swagger.annotations.Api;
/**
* The Class DiversityTreeController.
*
......
......@@ -54,14 +54,10 @@ public class ElasticQueryBuilder implements Visitor<Void, Void> {
private final Map<String, RangeQueryBuilder> ranges = new HashMap<>();
private final ElasticQueryBuilder self = this;
public QueryBuilder getQuery() {
BoolQueryBuilder root = QueryBuilders.boolQuery();
mustClauses.forEach(must -> root.filter(must));
mustNotClauses.forEach(mustNot -> root.mustNot(mustNot));
// shouldClauses.forEach(should -> root.should(should));
// if (shouldClauses.size() > 0) root.minimumNumberShouldMatch(1);
return root;
}
......@@ -124,10 +120,7 @@ public class ElasticQueryBuilder implements Visitor<Void, Void> {
if (operator == Ops.AND) {
handleAnd(args);
} else if (operator == Ops.OR) {
for (Expression<?> expr : args) {
printExpression(".. " + operator, expr);
expr.accept(self, null);
}
handleOr(args);
} else if (operator == Ops.EQ || operator == Ops.IN) {
if (Path.class.isAssignableFrom(args.get(0).getClass())) {
LOG.debug("EQUALS: {}", args);
......@@ -195,7 +188,7 @@ public class ElasticQueryBuilder implements Visitor<Void, Void> {
// printExpression("a1: " + type.getName() + " " + operator, a1);
}
private int size() {
public int size() {
return this.mustClauses.size() + this.mustNotClauses.size();
}
......@@ -211,15 +204,29 @@ public class ElasticQueryBuilder implements Visitor<Void, Void> {
mustNotClauses.addAll(andBuilder.mustNotClauses);
} else {
mustClauses.add(andBuilder.getQuery());
}
}
}
private void handleOr(List<Expression<?>> args) {
LOG.debug("OR expr: {}", args);
ElasticQueryBuilder orBuilder = new ElasticQueryBuilder();
for (Expression<?> a : args) {
a.accept(orBuilder, null);
}
BoolQueryBuilder orQuery = boolQuery();
orQuery.minimumShouldMatch(1);
orBuilder.mustClauses.forEach(should -> orQuery.should(should));
mustClauses.add(orQuery);
}
private void handleNot(Expression<?> notExp) {
LOG.debug("NOT expr: {}", notExp);
ElasticQueryBuilder notBuilder = new ElasticQueryBuilder();
notExp.accept(notBuilder, null);
// TODO Is this a problem?
notBuilder.mustClauses.forEach(mustNot -> mustNotClauses.add(mustNot));
notBuilder.mustNotClauses.forEach(must -> mustClauses.add(must));
}
private void handleLike(Operator operator, Path<?> path, Expression<?> val) {
......@@ -308,7 +315,7 @@ public class ElasticQueryBuilder implements Visitor<Void, Void> {
}
return obj;
}
private Collection<?> toValues(Expression<?> value) {
if (value instanceof Constant<?>) {
Constant<?> cons = (Constant<?>) value;
......
......@@ -125,6 +125,7 @@ public abstract class AccessionData extends AuditedVersionedModel implements IdU
@JoinColumn(name = "taxonomyId2")
@JsonView({ JsonViews.Minimal.class })
@Field(type = FieldType.Object)
@QueryInit({ "currentTaxonomySpecies.*", "grinTaxonomySpecies.*" })
private Taxonomy2 taxonomy;
// FIXME: Remove?
......
......@@ -17,14 +17,14 @@
package org.genesys2.server.model.genesys;
import java.text.MessageFormat;
import java.util.List;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.OneToMany;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.Transient;
......@@ -32,17 +32,17 @@ import javax.persistence.UniqueConstraint;
import org.apache.commons.lang3.StringUtils;
import org.genesys2.server.model.GlobalVersionedAuditedModel;