Commit e5610585 authored by LAVENIER's avatar LAVENIER
Browse files

[fix] Strategy: allow to filter by reference taxon id (need by IMAGINE)

parent ec64daab
......@@ -54,8 +54,6 @@ import net.sumaris.core.vo.filter.PmfmStrategyFilterVO;
import net.sumaris.core.vo.filter.StrategyFilterVO;
import net.sumaris.core.vo.referential.PmfmValueType;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
......@@ -67,7 +65,6 @@ import javax.persistence.PersistenceException;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.nuiton.i18n.I18n.t;
......@@ -528,7 +525,7 @@ public class ExtractionRdbTripDaoImpl<C extends ExtractionRdbTripContextVO, F ex
// Load strategies
List<ExtractionPmfmColumnVO> result = strategyService.findByFilter(StrategyFilterVO.builder()
.levelLabels(programLabels.toArray(new String[0]))
.programLabels(programLabels.toArray(new String[0]))
// TODO: filtrer les strategies via la periode du filtre (si présente) ?
// .startDate(...).endDate(...)
.build(), Pageable.unpaged(), StrategyFetchOptions.DEFAULT)
......
......@@ -103,7 +103,6 @@ public class Daos {
*/
public static final String DB_DIRECTORY = "db";
private static final boolean debug = log.isDebugEnabled();
/**
* <p>Constructor for Daos.</p>
......@@ -170,17 +169,11 @@ public class Daos {
statement.close();
} catch (SQLException ignored) {
}
if (debug) {
log.debug("Fix this linkage error, damned hsqlsb 1.8.0.7:(");
}
log.debug("Fix this linkage error, damned hsqlsb 1.8.0.7:(");
} catch (IllegalAccessError e) {
if (debug) {
log.debug("Fix this IllegalAccessError error, damned hsqlsb 1.8.0.7:(");
}
log.debug("Fix this IllegalAccessError error, damned hsqlsb 1.8.0.7:(");
} catch (Exception e) {
if (log.isErrorEnabled()) {
log.error("Could not close statement, but do not care", e);
}
log.error("Could not close statement, but do not care", e);
}
}
......@@ -217,13 +210,9 @@ public class Daos {
statement.close();
} catch (SQLException ignored) {
}
if (debug) {
log.debug("Fix this linkage error, damned hsqlsb 1.8.0.7:(");
}
log.debug("Fix this linkage error, damned hsqlsb 1.8.0.7:(");
} catch (IllegalAccessError e) {
if (debug) {
log.debug("Fix this IllegalAccessError error, damned hsqlsb 1.8.0.7:(");
}
log.debug("Fix this IllegalAccessError error, damned hsqlsb 1.8.0.7:(");
} catch (Exception e) {
if (log.isErrorEnabled()) {
log.error("Could not close statement, but do not care", e);
......@@ -972,9 +961,7 @@ public class Daos {
}
// Log using a special logger
if (debug) {
log.debug(sql);
}
log.debug(sql);
try {
return stmt.executeUpdate(sql);
......@@ -1061,9 +1048,7 @@ public class Daos {
}
// Log using a special logger
if (debug) {
log.debug(sql);
}
log.debug(sql);
try {
ResultSet rs = stmt.executeQuery(sql);
......@@ -1101,9 +1086,7 @@ public class Daos {
}
// Log using a special logger
if (debug) {
log.debug(sql);
}
log.debug(sql);
try {
ResultSet rs = stmt.executeQuery(sql);
......@@ -1205,10 +1188,7 @@ public class Daos {
*/
public static PreparedStatement prepareQuery(Connection connection, String sql) throws SQLException {
if (debug) {
log.debug(String.format("Execute query: %s", sql));
}
log.debug("Execute query: {}", sql);
return connection.prepareStatement(sql);
}
......@@ -1225,7 +1205,7 @@ public class Daos {
StringBuilder sb = new StringBuilder();
StringBuilder debugParams = null;
if (debug) {
if (log.isDebugEnabled()) {
debugParams = new StringBuilder();
}
......@@ -1247,9 +1227,7 @@ public class Daos {
.append("?");
offset = paramMatcher.end();
if (debug) {
debugParams.append(", ").append(bindingValue);
}
if (debugParams != null) debugParams.append(", ").append(bindingValue);
}
if (offset > 0) {
if (offset < sql.length()) {
......@@ -1258,10 +1236,10 @@ public class Daos {
sql = sb.toString();
}
if (debug) {
log.debug(String.format("Execute query: %s", sql));
log.debug(String.format(" with params: [%s]", debugParams.length() > 2 ? debugParams.substring(2)
: "no binding"));
if (debugParams != null) {
log.debug("Execute query: {}", sql);
log.debug(" with params: [{}]", debugParams.length() > 2 ? debugParams.substring(2)
: "no binding");
}
PreparedStatement statement = connection.prepareStatement(sql);
......@@ -1387,7 +1365,7 @@ public class Daos {
rs = statement.executeQuery();
if (rs.next()) {
Object result = rs.getObject(1);
if (result != null && result instanceof Number) {
if (result instanceof Number) {
return ((Number) result).longValue();
}
}
......
......@@ -26,10 +26,7 @@ import org.springframework.data.jpa.domain.Specification;
import javax.persistence.Parameter;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
......
package net.sumaris.core.dao.technical.jpa;
/*-
* #%L
* SUMARiS:: Core shared
* %%
* Copyright (C) 2018 - 2019 SUMARiS Consortium
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import javax.persistence.TupleElement;
public class Specifications {
protected Specifications() {
// Helper class
}
public static boolean isNumeric(TupleElement<?> path) {
return Number.class.isAssignableFrom(path.getJavaType());
}
}
package net.sumaris.cli.config;
/*-
* #%L
* Quadrige3 Core :: Quadrige3 Server Core
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2017 Ifremer
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import org.nuiton.config.ApplicationConfigProvider;
import org.nuiton.config.ConfigActionDef;
import org.nuiton.config.ConfigOptionDef;
import java.util.Locale;
import static org.nuiton.i18n.I18n.l;
/**
* Config provider (for site generation).
*
* @author Benoit Lavenier <benoit.lavenier@e-is.pro>
* @since 1.0
*/
public class SumarisCliConfigurationProvider implements ApplicationConfigProvider {
/** {@inheritDoc} */
@Override
public String getName() {
return "sumaris-cli";
}
/** {@inheritDoc} */
@Override
public String getDescription(Locale locale) {
return l(locale, "sumaris.cli.config.actions");
}
/** {@inheritDoc} */
@Override
public ConfigOptionDef[] getOptions() {
return new ConfigOptionDef[0];
}
/** {@inheritDoc} */
@Override
public ConfigActionDef[] getActions() {
return SumarisCliConfigurationAction.values();
}
}
......@@ -277,7 +277,8 @@ public class StrategyRepositoryImpl
super.onBeforeSaveEntity(vo, entity, isNew);
// Verify label is unique by program
long count = this.findAll(StrategyFilterVO.builder().programId(vo.getProgramId()).label(vo.getLabel()).build())
long count = this.findAll(StrategyFilterVO.builder()
.programIds(new Integer[]{vo.getProgramId()}).label(vo.getLabel()).build())
.stream()
.filter(s -> isNew || !Objects.equals(s.getId(), vo.getId()))
.count();
......@@ -346,7 +347,11 @@ public class StrategyRepositoryImpl
Specification<Strategy> spec = super.toSpecification(filter, fetchOptions);
if (filter.getId() != null) return spec;
return spec
.and(hasProgramIds(filter))
// Not need, has already defined using levelIds, in the super function
//.and(hasProgramIds(filter))
.and(hasReferenceTaxonIds(filter.getReferenceTaxonIds()))
.and(betweenDate(filter.getStartDate(), filter.getEndDate()));
}
......
......@@ -22,23 +22,21 @@ package net.sumaris.core.dao.administration.programStrategy;
* #L%
*/
import net.sumaris.core.dao.referential.ReferentialSpecifications;
import net.sumaris.core.dao.technical.jpa.BindableSpecification;
import net.sumaris.core.model.administration.programStrategy.AppliedPeriod;
import net.sumaris.core.model.administration.programStrategy.AppliedStrategy;
import net.sumaris.core.model.administration.programStrategy.ProgramPrivilegeEnum;
import net.sumaris.core.model.administration.programStrategy.Strategy;
import net.sumaris.core.model.data.Landing;
import net.sumaris.core.model.administration.programStrategy.*;
import net.sumaris.core.model.referential.Status;
import net.sumaris.core.model.referential.taxon.ReferenceTaxon;
import net.sumaris.core.vo.administration.programStrategy.*;
import net.sumaris.core.vo.filter.StrategyFilterVO;
import net.sumaris.core.vo.referential.ReferentialVO;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.Parameter;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
......@@ -47,28 +45,13 @@ import java.util.List;
/**
* @author peck7 on 24/08/2020.
*/
public interface StrategySpecifications {
public interface StrategySpecifications extends ReferentialSpecifications<Strategy> {
String PROGRAM_IDS_PARAM = "programIds";
String HAS_PROGRAM_PARAM = "hasProgram";
String REFERENCE_TAXON_IDS = "referenceTaxonIds";
String UPDATE_DATE_GREATER_THAN_PARAM = "updateDateGreaterThan";
default Specification<Strategy> hasProgramIds(StrategyFilterVO filter) {
return hasProgramIds(filter.getProgramId() != null ? new Integer[]{filter.getProgramId()} : filter.getProgramIds());
}
default Specification<Strategy> hasProgramIds(Integer... programIds) {
BindableSpecification<Strategy> specification = BindableSpecification.where((root, query, criteriaBuilder) -> {
ParameterExpression<Collection> programIdsParam = criteriaBuilder.parameter(Collection.class, PROGRAM_IDS_PARAM);
ParameterExpression<Boolean> hasProgramParam = criteriaBuilder.parameter(Boolean.class, HAS_PROGRAM_PARAM);
return criteriaBuilder.or(
criteriaBuilder.isFalse(hasProgramParam),
criteriaBuilder.in(root.get(Strategy.Fields.PROGRAM).get(Status.Fields.ID)).value(programIdsParam)
);
});
specification.addBind(HAS_PROGRAM_PARAM, !ArrayUtils.isEmpty(programIds));
specification.addBind(PROGRAM_IDS_PARAM, ArrayUtils.isEmpty(programIds) ? null : Arrays.asList(programIds));
return specification;
return inLevelIds(Strategy.class, programIds);
}
default Specification<Strategy> newerThan(Date updateDate) {
......@@ -80,6 +63,24 @@ public interface StrategySpecifications {
return specification;
}
default Specification<Strategy> hasReferenceTaxonIds(Integer... referenceTaxonIds) {
if (ArrayUtils.isEmpty(referenceTaxonIds)) return null;
BindableSpecification<Strategy> specification = BindableSpecification.where((root, query, criteriaBuilder) -> {
// Avoid duplictaed entries (because of inner join)
query.distinct(true);
ParameterExpression<Collection> referenceTaxonIdsParam = criteriaBuilder.parameter(Collection.class, REFERENCE_TAXON_IDS);
return criteriaBuilder.in(
root.join(Strategy.Fields.REFERENCE_TAXONS, JoinType.INNER)
.join(ReferenceTaxonStrategy.Fields.REFERENCE_TAXON, JoinType.INNER)
.get(ReferenceTaxon.Fields.ID))
.value(referenceTaxonIdsParam);
});
specification.addBind(REFERENCE_TAXON_IDS, Arrays.asList(referenceTaxonIds));
return specification;
}
default Specification<Strategy> betweenDate(Date startDate, Date endDate) {
if (startDate == null && endDate == null) return null;
return (root, query, cb) -> {
......
......@@ -498,9 +498,7 @@ public class ReferentialDaoImpl
IReferentialFilter filter
//QueryVisitor<R, T> queryVisitor
) {
Integer levelId = filter.getLevelId();
Integer[] levelIds = filter.getLevelIds();
String levelLabel = filter.getLevelLabel();
String[] levelLabels = filter.getLevelLabels();
String searchText = StringUtils.trimToNull(filter.getSearchText());
String searchAttribute = StringUtils.trimToNull(filter.getSearchAttribute());
......@@ -512,28 +510,12 @@ public class ReferentialDaoImpl
Predicate levelIdClause = null;
ParameterExpression<Collection> levelIdsParam = null;
if (ArrayUtils.isNotEmpty(levelIds)) {
if (levelIds.length == 1) {
levelId = levelIds[0];
levelIds = null;
} else {
levelId = null;
levelIdsParam = builder.parameter(Collection.class);
String levelPropertyName = ReferentialEntities.getLevelPropertyName(entityClass.getSimpleName()).orElse(null);
if (levelPropertyName != null) {
levelIdClause = builder.in(entityRoot.get(levelPropertyName).get(IReferentialEntity.Fields.ID)).value(levelIdsParam);
} else {
log.warn(String.format("Trying to request on level, but no level found for entity {%s}", entityClass.getSimpleName()));
}
}
}
ParameterExpression<Integer> levelIdParam = null;
if (levelId != null) {
levelIdParam = builder.parameter(Integer.class);
levelIdsParam = builder.parameter(Collection.class);
String levelPropertyName = ReferentialEntities.getLevelPropertyName(entityClass.getSimpleName()).orElse(null);
if (levelPropertyName != null) {
levelIdClause = builder.equal(entityRoot.get(levelPropertyName).get(IReferentialEntity.Fields.ID), levelIdParam);
levelIdClause = builder.in(entityRoot.get(levelPropertyName).get(IReferentialEntity.Fields.ID)).value(levelIdsParam);
} else {
log.warn(String.format("Trying to request on level, but no level found for entity {%s}", entityClass.getSimpleName()));
log.warn(String.format("Trying to request on level, but no level found for entity {%s}", entityClass.getSimpleName()));
}
}
......@@ -541,28 +523,12 @@ public class ReferentialDaoImpl
Predicate levelLabelClause = null;
ParameterExpression<Collection> levelLabelsParam = null;
if (ArrayUtils.isNotEmpty(levelLabels)) {
if (levelLabels.length == 1) {
levelLabel = levelLabels[0];
levelLabels = null;
} else {
levelLabel = null;
levelLabelsParam = builder.parameter(Collection.class);
String levelPropertyName = ReferentialEntities.getLevelPropertyName(entityClass.getSimpleName()).orElse(null);
if (levelPropertyName != null) {
levelLabelClause = builder.in(entityRoot.get(levelPropertyName).get(IItemReferentialEntity.Fields.LABEL)).value(levelLabelsParam);
} else {
log.warn(String.format("Trying to request on level, but no level found for entity {%s}", entityClass.getSimpleName()));
}
}
}
ParameterExpression<String> levelLabelParam = null;
if (StringUtils.isNotBlank(levelLabel)) {
levelLabelParam = builder.parameter(String.class);
levelLabelsParam = builder.parameter(Collection.class);
String levelPropertyName = ReferentialEntities.getLevelPropertyName(entityClass.getSimpleName()).orElse(null);
if (levelPropertyName != null) {
levelLabelClause = builder.equal(entityRoot.get(levelPropertyName).get(IItemReferentialEntity.Fields.LABEL), levelLabelParam);
levelLabelClause = builder.in(entityRoot.get(levelPropertyName).get(IItemReferentialEntity.Fields.LABEL)).value(levelLabelsParam);
} else {
log.warn(String.format("Trying to request on level, but no level found for entity {%s}", entityClass.getSimpleName()));
log.warn(String.format("Trying to request on level, but no level found for entity {%s}", entityClass.getSimpleName()));
}
}
......@@ -695,19 +661,11 @@ public class ReferentialDaoImpl
typedQuery.setParameter(searchAsPrefixParam, searchTextAsPrefix);
typedQuery.setParameter(searchAnyMatchParam, searchTextAnyMatch);
}
if (levelIdClause != null) {
if (levelIds != null) {
typedQuery.setParameter(levelIdsParam, ImmutableList.copyOf(levelIds));
} else {
typedQuery.setParameter(levelIdParam, levelId);
}
if (levelIdClause != null && levelIds != null) {
typedQuery.setParameter(levelIdsParam, ImmutableList.copyOf(levelIds));
}
if (levelLabelClause != null) {
if (levelLabels != null) {
typedQuery.setParameter(levelLabelsParam, ImmutableList.copyOf(levelLabels));
} else {
typedQuery.setParameter(levelLabelParam, levelLabel);
}
if (levelLabelClause != null && levelLabels != null) {
typedQuery.setParameter(levelLabelsParam, ImmutableList.copyOf(levelLabels));
}
if (statusIdsClause != null) {
typedQuery.setParameter(statusIdsParam, ImmutableList.copyOf(statusIds));
......
......@@ -249,12 +249,13 @@ public abstract class ReferentialRepositoryImpl<E extends IItemReferentialEntity
if (filter.getId() != null) {
return BindableSpecification.where(hasId(filter.getId()));
}
Class<E> clazz = getDomainClass();
// default specification
return BindableSpecification
.where(inStatusIds(filter))
.and(hasLabel(filter.getLabel()))
.and(inLevelIds(getDomainClass(), filter.getLevelIds()))
.and(inLevelLabels(getDomainClass(), filter.getLevelLabels()))
.and(inLevelIds(clazz, filter.getLevelIds()))
.and(inLevelLabels(clazz, filter.getLevelLabels()))
.and(searchOrJoinSearchText(filter))
.and(includedIds(filter.getIncludedIds()))
.and(excludedIds(filter.getExcludedIds()));
......
......@@ -127,14 +127,13 @@ public interface ReferentialSpecifications<E extends IReferentialWithStatusEntit
}
default Specification<E> searchOrJoinSearchText(IReferentialFilter filter) {
String searchText = Daos.getEscapedSearchText(filter.getSearchText());
String searchJoinProperty = filter.getSearchJoin() != null ? StringUtils.uncapitalize(filter.getSearchJoin()) : null;
if (StringUtils.isNotBlank(searchJoinProperty)) {
return joinSearchText(searchJoinProperty, filter.getSearchAttribute(), searchText);
return joinSearchText(searchJoinProperty, filter.getSearchAttribute(), filter.getSearchText());
} else {
return searchText(
StringUtils.isNotBlank(filter.getSearchAttribute()) ? ArrayUtils.toArray(filter.getSearchAttribute()) : null,
searchText);
filter.getSearchText());
}
}
......@@ -163,7 +162,7 @@ public interface ReferentialSpecifications<E extends IReferentialWithStatusEntit
criteriaBuilder.like(criteriaBuilder.upper(root.get(IItemReferentialEntity.Fields.NAME)), criteriaBuilder.upper(criteriaBuilder.concat("%", searchTextParam)))
);
});
specification.addBind(SEARCH_TEXT_PARAMETER, searchText);
specification.addBind(SEARCH_TEXT_PARAMETER, Daos.getEscapedSearchText(searchText));
return specification;
}
......@@ -182,11 +181,13 @@ public interface ReferentialSpecifications<E extends IReferentialWithStatusEntit
join = join.join(joinProperties[i], JoinType.INNER);
}
// Search on given attribute
if (StringUtils.isNotBlank(searchAttribute)) {
return criteriaBuilder.or(
criteriaBuilder.isNull(searchTextParam),
criteriaBuilder.like(criteriaBuilder.upper(join.get(searchAttribute)), criteriaBuilder.upper(searchTextParam)));
}
// Search on label+name
return criteriaBuilder.or(
criteriaBuilder.isNull(searchTextParam),
......@@ -194,7 +195,7 @@ public interface ReferentialSpecifications<E extends IReferentialWithStatusEntit
criteriaBuilder.like(criteriaBuilder.upper(join.get(IItemReferentialEntity.Fields.NAME)), criteriaBuilder.upper(criteriaBuilder.concat("%", searchTextParam)))
);
});
specification.addBind(SEARCH_TEXT_PARAMETER, searchText);
specification.addBind(SEARCH_TEXT_PARAMETER, Daos.getEscapedSearchText(searchText));
return specification;
}
......
......@@ -343,13 +343,16 @@ public class TaxonGroupRepositoryImpl
@Override
protected Specification<TaxonGroup> toSpecification(IReferentialFilter filter, ReferentialFetchOptions fetchOptions) {
Preconditions.checkNotNull(filter);
Integer[] gearIds = filter.getLevelId() != null
? new Integer[]{filter.getLevelId()}
: filter.getLevelIds();
Integer[] gearIds = filter.getLevelIds();
filter.setLevelIds(null);
return super.toSpecification(filter, fetchOptions)
Specification<TaxonGroup> result = super.toSpecification(filter, fetchOptions)
.and(hasType(TaxonGroupTypeEnum.METIER_SPECIES.getId()))
.and(inGearIds(gearIds));
// restore levelIds
filter.setLevelIds(gearIds);