TaxonomySpeciesCRUDServiceImpl.java
/*
* Copyright 2020 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.service.impl;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.gringlobal.api.exception.InvalidApiUsageException;
import org.gringlobal.custom.elasticsearch.SearchException;
import org.gringlobal.model.QTaxonomySpecies;
import org.gringlobal.model.TaxonomyGenus;
import org.gringlobal.model.TaxonomySpecies;
import org.gringlobal.persistence.TaxonomySpeciesRepository;
import org.gringlobal.service.TaxonomyGenusCRUDService;
import org.gringlobal.service.TaxonomySpeciesCRUDService;
import org.gringlobal.service.filter.TaxonomySpeciesFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.jpa.impl.JPAQuery;
/**
* The Class TaxonomySpeciesCRUDService.
*/
@Service
@Slf4j
public class TaxonomySpeciesCRUDServiceImpl extends FilteredCRUDService2Impl<TaxonomySpecies, TaxonomySpeciesFilter, TaxonomySpeciesRepository> implements
TaxonomySpeciesCRUDService {
final static Pattern PATTERN_SUBSP = Pattern.compile("subsp\\.\\s*(\\w+)");
final static Pattern PATTERN_VAR = Pattern.compile("var\\.\\s*(\\w+)");
final static Pattern PATTERN_SUBV = Pattern.compile("subvar\\.\\s*(\\w+)");
final static Pattern PATTERN_FORM = Pattern.compile("f\\.\\s*(\\w+)");
private static final String[] BOOST_FIELDS = { "speciesName", "name", "taxonomyGenus.genusName" };
@Autowired
private TaxonomyGenusCRUDService taxonomyGenusService;
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Taxonomy', 'CREATE')")
public TaxonomySpecies create(TaxonomySpecies source) {
TaxonomySpecies record = new TaxonomySpecies();
record.apply(source);
record.setTaxonomyGenus(taxonomyGenusService.get(record.getTaxonomyGenus().getId()));
record = repository.save(record);
if (record.getCurrentTaxonomySpecies() == null) {
record.setCurrentTaxonomySpecies(record);
record = repository.save(record);
}
return _lazyLoad(record);
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Taxonomy', 'CREATE')")
public TaxonomySpecies createFast(TaxonomySpecies source) {
TaxonomySpecies record = new TaxonomySpecies();
record.apply(source);
record.setTaxonomyGenus(taxonomyGenusService.get(record.getTaxonomyGenus().getId()));
record = repository.save(record);
if (record.getCurrentTaxonomySpecies() == null) {
record.setCurrentTaxonomySpecies(record);
record = repository.save(record);
}
return record;
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Taxonomy', 'ADMINISTRATION')")
public TaxonomySpecies update(TaxonomySpecies updated, TaxonomySpecies target) {
if (target.getGrinId() != null) {
throw new InvalidApiUsageException("TaxonomySpecies records sourced from GRIN Taxonomy are unmodifiable");
}
target.apply(updated);
target.setTaxonomyGenus(taxonomyGenusService.get(target.getTaxonomyGenus().getId()));
return _lazyLoad(repository.save(target));
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Taxonomy', 'ADMINISTRATION')")
public TaxonomySpecies updateFast(TaxonomySpecies updated, TaxonomySpecies target) {
if (target.getGrinId() != null) {
throw new InvalidApiUsageException("TaxonomySpecies records sourced from GRIN Taxonomy are unmodifiable");
}
target.apply(updated);
target.setTaxonomyGenus(taxonomyGenusService.get(target.getTaxonomyGenus().getId()));
return repository.save(target);
}
@Override
@Transactional
@PreAuthorize("hasAuthority('GROUP_ADMINS') or @ggceSec.actionAllowed('Taxonomy', 'DELETE')")
public TaxonomySpecies remove(TaxonomySpecies entity) {
return super.remove(entity);
}
@Override
public Page<TaxonomySpecies> list(TaxonomySpeciesFilter filter, Pageable page) throws SearchException {
return super.list(TaxonomySpecies.class, filter, page, BOOST_FIELDS);
}
@Override
protected JPAQuery<TaxonomySpecies> entityListQuery() {
return jpaQueryFactory.selectFrom(QTaxonomySpecies.taxonomySpecies)
//
.join(QTaxonomySpecies.taxonomySpecies.taxonomyGenus()).fetchJoin()
// owner
.join(QTaxonomySpecies.taxonomySpecies.ownedBy()).fetchJoin();
}
@Override
protected NumberPath<Long> entityIdPredicate() {
return QTaxonomySpecies.taxonomySpecies.id;
}
@Override
@Transactional(readOnly = false)
public TaxonomySpecies fromMCPD(String genus, String specificEpithet, String spAuthor, String subTaxa, String subtAuthor) {
// LOG.warn("Ensuring taxonomy {} {} {} {} {}", genus, specificEpithet, spAuthor, subTaxa, subtAuthor);
if (StringUtils.startsWithIgnoreCase(genus, "X")) {
// Trim starting hybid code
genus = genus.substring(1).strip();
}
if ("sp.".equalsIgnoreCase(specificEpithet)) {
// GRIN Taxonomy style
specificEpithet = "spp.";
}
String subsp1 = null;
String var1 = null;
String subvar1 = null;
String forma1 = null;
if (StringUtils.isNotBlank(subTaxa)) {
Matcher m = PATTERN_SUBSP.matcher(subTaxa);
if (m.find()) {
subsp1 = m.group(1);
}
m = PATTERN_VAR.matcher(subTaxa);
if (m.find()) {
var1 = m.group(1);
}
m = PATTERN_SUBV.matcher(subTaxa);
if (m.find()) {
subvar1 = m.group(1);
}
m = PATTERN_FORM.matcher(subTaxa);
if (m.find()) {
forma1 = m.group(1);
}
}
final String subsp = StringUtils.defaultIfBlank(subsp1, null);
final String varValue = StringUtils.defaultIfBlank(var1, null);
final String subvar = StringUtils.defaultIfBlank(subvar1, null);
final String forma = StringUtils.defaultIfBlank(forma1, null);
List<TaxonomySpecies> matches = jpaQueryFactory.selectFrom(QTaxonomySpecies.taxonomySpecies).where(
// species name
QTaxonomySpecies.taxonomySpecies.speciesName.eq(specificEpithet)
// genus
.and(QTaxonomySpecies.taxonomySpecies.taxonomyGenus().genusName.eq(genus))
// emm
).fetch();
log.debug("Got {} matches for {} {}", matches.size(), genus, specificEpithet);
TaxonomySpecies best = null;
TaxonomySpecies topLevel = null;
if (matches.size() > 0) {
for (TaxonomySpecies match : matches) {
log.debug("Match {} {} {} {} {} ?== {} <== subsp.{} var.{} subvar.{} f.{}", genus, specificEpithet, spAuthor, subTaxa, subtAuthor, match.getName(), match
.getSubspeciesName(), match.getVarietyName(), match.getSubvarietyName(), match.getFormaName());
if (subsp != null && subsp.equalsIgnoreCase(match.getSubspeciesName())) {
best = match;
}
if (varValue != null && varValue.equalsIgnoreCase(match.getVarietyName())) {
best = match;
}
if (subvar != null && varValue.equalsIgnoreCase(match.getSubvarietyName())) {
best = match;
}
if (forma != null && forma.equalsIgnoreCase(match.getFormaName())) {
best = match;
}
if (best == null && match.getSubspeciesName() == null && match.getVarietyName() == null && match.getSubvarietyName() == null && match.getFormaName() == null) {
// Toplevel name if nothing else
best = topLevel = match;
}
}
if (best != null) {
if (topLevel == best && (subsp != null || varValue != null || subvar != null || forma != null)) {
// Incoming species has details, make new record.
} else {
log.info("Best match {} {} {} {} {} ?== {} <== subsp.{} var.{} subvar.{} f.{}", genus, specificEpithet, spAuthor, subTaxa, subtAuthor, best.getName(), best.getSubspeciesName(), best.getVarietyName(), best.getSubvarietyName(), best.getFormaName());
return best;
}
}
}
TaxonomyGenus taxonomyGenus = taxonomyGenusService.getGenus(genus, null);
if (taxonomyGenus == null) {
return null;
}
// Figure out authority name on lowest level
String subspAuthority = null;
String varAuthority = null;
String subvarAuthority = null;
String formaAuthority = null;
if (StringUtils.isNotBlank(subtAuthor)) {
if (forma != null) {
formaAuthority = subtAuthor;
} else if (subvar != null) {
subvarAuthority = subtAuthor;
} else if (varValue != null) {
varAuthority = subtAuthor;
} else if (subsp != null) {
subspAuthority = subtAuthor;
}
}
TaxonomySpecies taxonomySpecies = new TaxonomySpecies();
taxonomySpecies.setTaxonomyGenus(taxonomyGenus);
taxonomySpecies.setSpeciesName(specificEpithet);
taxonomySpecies.setSpeciesAuthority(spAuthor);
taxonomySpecies.setSubspeciesName(subsp);
taxonomySpecies.setVarietyAuthority(subspAuthority);
taxonomySpecies.setVarietyName(varValue);
taxonomySpecies.setVarietyAuthority(varAuthority);
taxonomySpecies.setSubvarietyName(subvar);
taxonomySpecies.setFormaAuthority(subvarAuthority);
taxonomySpecies.setFormaName(forma);
taxonomySpecies.setFormaAuthority(formaAuthority);
taxonomySpecies.setFormaRankType(forma == null ? null : "f.");
taxonomySpecies.setNote("From MCPD " + genus + " " + specificEpithet + " " + spAuthor + " " + subTaxa + " " + subtAuthor);
log.info("New record {} {} {} {} {}", genus, specificEpithet, spAuthor, subTaxa, subtAuthor);
return repository.save(taxonomySpecies);
}
}