Commit 77631480 authored by RANNOU's avatar RANNOU 👽
Browse files

[enh] Service/Dao: check if sample TAG_ID is unique on save (#385)

parent 742b3f80
......@@ -900,6 +900,10 @@ public class SumarisConfiguration extends PropertyPlaceholderConfigurer {
return applicationConfig.getOptionAsBoolean(SumarisConfigurationOption.ENABLE_SAMPLE_HASH_OPTIMIZATION.getKey());
}
public boolean enableSampleUniqueTag() {
return applicationConfig.getOptionAsBoolean(SumarisConfigurationOption.ENABLE_SAMPLE_UNIQUE_TAG.getKey());
}
public String getVesselDefaultProgramLabel() {
return applicationConfig.getOption(SumarisConfigurationOption.VESSEL_DEFAULT_PROGRAM_LABEL.getKey());
}
......
......@@ -571,6 +571,13 @@ public enum SumarisConfigurationOption implements ConfigOptionDef {
Boolean.class,
false),
ENABLE_SAMPLE_UNIQUE_TAG(
"sumaris.persistence.sample.uniqueTag",
n("sumaris.config.option.persistence.sample.uniqueTag.description"),
Boolean.FALSE.toString(),
Boolean.class,
false),
VESSEL_DEFAULT_PROGRAM_LABEL(
"sumaris.persistence.vessel.defaultProgram.label",
n("sumaris.config.option.persistence.vessel.defaultProgram.label.description"),
......
......@@ -32,9 +32,11 @@ import net.sumaris.core.dao.referential.taxon.TaxonNameRepository;
import net.sumaris.core.dao.technical.Daos;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.event.config.ConfigurationUpdatedEvent;
import net.sumaris.core.exception.NotUniqueException;
import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.core.model.data.*;
import net.sumaris.core.model.referential.pmfm.Matrix;
import net.sumaris.core.model.referential.pmfm.PmfmEnum;
import net.sumaris.core.model.referential.pmfm.Unit;
import net.sumaris.core.model.referential.pmfm.UnitEnum;
import net.sumaris.core.model.referential.taxon.ReferenceTaxon;
......@@ -77,6 +79,7 @@ public class SampleRepositoryImpl
private TaxonNameRepository taxonNameRepository;
private boolean enableSaveUsingHash;
private boolean enableCheckUniqueTag;
@Autowired
public SampleRepositoryImpl(EntityManager entityManager) {
......@@ -89,6 +92,7 @@ public class SampleRepositoryImpl
@EventListener({ConfigurationReadyEvent.class, ConfigurationUpdatedEvent.class})
public void onConfigurationReady() {
this.enableSaveUsingHash = getConfig().enableSampleHashOptimization();
this.enableCheckUniqueTag = getConfig().enableSampleUniqueTag();
}
@Override
......@@ -98,6 +102,7 @@ public class SampleRepositoryImpl
.and(hasLandingId(filter.getLandingId()))
.and(hasObservedLocationId(filter.getObservedLocationId()))
.and(inObservedLocationIds(filter.getObservedLocationIds()))
.and(hasTagId(filter.getTagId()))
.and(addJoinFetch(fetchOptions, true));
}
......@@ -304,6 +309,18 @@ public class SampleRepositoryImpl
log.warn(String.format("Updating a sample {id: %s, label: '%s'} without creation_date!", entity.getId(), entity.getLabel()));
}
super.onBeforeSaveEntity(vo, entity, isNew);
// Check if tag_id is unique by program
if (enableCheckUniqueTag) {
long count = this.findAll(SampleFilterVO.builder()
.programLabel(vo.getProgram().getLabel()).tagId(vo.getMeasurementValues().get(PmfmEnum.TAG_ID)).build())
.stream()
.filter(s -> isNew || !Objects.equals(s.getId(), vo.getId()))
.count();
if (count > 0) {
throw new NotUniqueException(String.format("Sample tag '%s' already exists", vo.getLabel()));
}
}
}
@Override
......
......@@ -27,12 +27,15 @@ import net.sumaris.core.dao.technical.jpa.BindableSpecification;
import net.sumaris.core.dao.technical.model.IEntity;
import net.sumaris.core.model.data.Landing;
import net.sumaris.core.model.data.Sample;
import net.sumaris.core.model.data.SampleMeasurement;
import net.sumaris.core.model.referential.pmfm.PmfmEnum;
import net.sumaris.core.vo.data.LandingVO;
import net.sumaris.core.vo.data.sample.SampleFetchOptions;
import net.sumaris.core.vo.data.sample.SampleVO;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ParameterExpression;
import java.util.Arrays;
......@@ -45,6 +48,8 @@ import java.util.List;
public interface SampleSpecifications extends RootDataSpecifications<Sample> {
String OBSERVED_LOCATION_IDS = "observedLocationIds";
String TAG_ID_PMFM_ID = "tagIdPmfmId";
String TAG_ID = "tagId";
default Specification<Sample> hasOperationId(Integer operationId) {
if (operationId == null) return null;
......@@ -101,6 +106,23 @@ public interface SampleSpecifications extends RootDataSpecifications<Sample> {
return specification;
}
default Specification<Sample> hasTagId(String tagId) {
if (tagId == null) return null;
BindableSpecification<Sample> specification = BindableSpecification.where((root, query, criteriaBuilder) -> {
query.orderBy(criteriaBuilder.asc(root.get(Sample.Fields.RANK_ORDER)));
ParameterExpression<Integer> tagIdPmfmIdParam = criteriaBuilder.parameter(Integer.class, TAG_ID_PMFM_ID);
ParameterExpression<String> tagIdParam = criteriaBuilder.parameter(String.class, TAG_ID);
Join<Sample, SampleMeasurement> tagIdInnerJoin = root.joinList(Sample.Fields.MEASUREMENTS, JoinType.INNER);
return criteriaBuilder.and(
criteriaBuilder.equal(tagIdInnerJoin.get(SampleMeasurement.Fields.PMFM).get(IEntity.Fields.ID), tagIdPmfmIdParam),
criteriaBuilder.equal(tagIdInnerJoin.get(SampleMeasurement.Fields.ALPHANUMERICAL_VALUE), tagIdParam)
);
});
specification.addBind(TAG_ID_PMFM_ID, PmfmEnum.TAG_ID.getId());
specification.addBind(TAG_ID, tagId);
return specification;
}
default Specification<Sample> addJoinFetch(SampleFetchOptions fetchOptions, boolean addQueryDistinct) {
if (fetchOptions == null || !fetchOptions.isWithMeasurementValues()) return null;
......
......@@ -62,4 +62,6 @@ public class SampleFilterVO implements IRootDataFilter {
private Integer parentId;
private String tagId;
}
......@@ -889,7 +889,6 @@
<!-- PARAMBIO / Strategy -->
<PARAMETER ID="350" LABEL="AGE" NAME="Âge" IS_BOOLEAN="0" IS_QUALITATIVE="0" IS_ALPHANUMERIC="0" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<PARAMETER ID="351" LABEL="MATURITY_STAGE_9_VISUAL" NAME="Maturité sexuelle à 4 plus 2 stades" IS_BOOLEAN="0" IS_QUALITATIVE="1" IS_ALPHANUMERIC="0" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<!--<PARAMETER ID="352" LABEL="MORSE_CODE" NAME="Code Morse" IS_QUALITATIVE="0" IS_ALPHANUMERIC="1" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>-->
<PARAMETER ID="353" LABEL="STRATEGY_LABEL" NAME="Stratégie" IS_QUALITATIVE="0" IS_ALPHANUMERIC="1" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<!-- QV / Trawl type-->
......@@ -1469,9 +1468,7 @@
<PMFM ID="351" PARAMETER_FK="351" LABEL="MATURITY_STAGE_9_VISUAL" MATRIX_FK="2" FRACTION_FK="1" METHOD_FK="2" UNIT_FK="0" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<PMFM ID="356" PARAMETER_FK="91" LABEL="WEIGHT_TOTAL_KG" MATRIX_FK="2" FRACTION_FK="1" METHOD_FK="1" MIN_VALUE="0" MAXIMUM_NUMBER_DECIMALS="2" UNIT_FK="3" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<PMFM ID="357" PARAMETER_FK="91" LABEL="WEIGHT_ESTIMATED_TOTAL_KG" MATRIX_FK="2" FRACTION_FK="1" METHOD_FK="3" MIN_VALUE="0" MAXIMUM_NUMBER_DECIMALS="2" UNIT_FK="3" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<!--<PMFM ID="358" PARAMETER_FK="352" LABEL="MORSE_CODE" UNIT_FK="0" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>-->
<PMFM ID="359" PARAMETER_FK="353" LABEL="STRATEGY_LABEL" UNIT_FK="0" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<PMFM ID="1435" PARAMETER_FK="82" LABEL="SAMPLE_ID" UNIT_FK="0" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
<!-- Round weight conversion -->
<ROUND_WEIGHT_CONVERSION ID="1" TAXON_GROUP_FK="1122" CONVERSION_COEFFICIENT="3.95" START_DATE="2010-01-01 00:00:00.000000" LOCATION_FK="1" DRESSING_FK="351" PRESERVING_FK="332" STATUS_FK="1" CREATION_DATE="2018-03-08" UPDATE_DATE="2018-03-08"/>
......
......@@ -274,6 +274,8 @@
<SAMPLE ID="50" LANDING_FK="30" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" SAMPLE_DATE="2020-12-01 16:00:00.000" CREATION_DATE="2020-12-18" UPDATE_DATE="2018-03-08" LABEL="20-LEUCCIR-001-0001" RANK_ORDER="1" MATRIX_FK="2" QUALITY_FLAG_FK="0" REFERENCE_TAXON_FK="1010" COMMENTS="sample test #50"/>
<SAMPLE ID="51" LANDING_FK="30" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" SAMPLE_DATE="2020-12-01 16:00:00.000" CREATION_DATE="2020-12-18" UPDATE_DATE="2018-03-08" LABEL="20-LEUCCIR-001-0002" RANK_ORDER="2" MATRIX_FK="2" QUALITY_FLAG_FK="0" REFERENCE_TAXON_FK="1010" COMMENTS="sample test #51"/>
<!-- tag_id -->
<SAMPLE_MEASUREMENT ID="50" SAMPLE_FK="50" RECORDER_DEPARTMENT_FK="2" PMFM_FK="82" RANK_ORDER="1" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" ALPHANUMERICAL_VALUE="20-LEUCCIR-001-0001"/>
<!-- poids -->
<SAMPLE_MEASUREMENT ID="51" SAMPLE_FK="50" RECORDER_DEPARTMENT_FK="2" PMFM_FK="356" RANK_ORDER="2" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="4"/>
<!-- taille -->
......@@ -282,13 +284,17 @@
<SAMPLE_MEASUREMENT ID="53" SAMPLE_FK="50" RECORDER_DEPARTMENT_FK="2" PMFM_FK="350" RANK_ORDER="4" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="14"/>
<!-- autre -->
<SAMPLE_MEASUREMENT ID="54" SAMPLE_FK="50" RECORDER_DEPARTMENT_FK="2" PMFM_FK="80" RANK_ORDER="5" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" QUALITATIVE_VALUE_FK="185"/>
<!-- tag_id -->
<SAMPLE_MEASUREMENT ID="55" SAMPLE_FK="51" RECORDER_DEPARTMENT_FK="2" PMFM_FK="82" RANK_ORDER="1" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" ALPHANUMERICAL_VALUE="20-LEUCCIR-001-0002"/>
<!-- taille -->
<SAMPLE_MEASUREMENT ID="55" SAMPLE_FK="51" RECORDER_DEPARTMENT_FK="2" PMFM_FK="81" RANK_ORDER="6" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="10"/>
<SAMPLE_MEASUREMENT ID="56" SAMPLE_FK="51" RECORDER_DEPARTMENT_FK="2" PMFM_FK="81" RANK_ORDER="6" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="10"/>
<LANDING ID="31" OBSERVED_LOCATION_FK="12" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" CREATION_DATE="2020-12-18" UPDATE_DATE="2018-03-08" LANDING_DATE_TIME="2020-12-01 16:00:00.000" LANDING_LOCATION_FK="30" QUALITY_FLAG_FK="0" RANK_ORDER="1" VESSEL_FK="1" COMMENTS="landing test #2"/>
<LANDING_MEASUREMENT ID="31" LANDING_FK="31" PMFM_FK="359" RANK_ORDER="1" ALPHANUMERICAL_VALUE="20-LEUCCIR-001" UPDATE_DATE="2018-03-08" RECORDER_DEPARTMENT_FK="2" QUALITY_FLAG_FK="0"/>
<SAMPLE ID="52" LANDING_FK="31" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" SAMPLE_DATE="2020-12-01 16:00:00.000" CREATION_DATE="2020-12-18" UPDATE_DATE="2018-03-08" LABEL="20-LEUCCIR-001-0003" RANK_ORDER="1" MATRIX_FK="2" QUALITY_FLAG_FK="0" REFERENCE_TAXON_FK="1010" COMMENTS="sample test #52"/>
<!-- tag_id -->
<SAMPLE_MEASUREMENT ID="60" SAMPLE_FK="52" RECORDER_DEPARTMENT_FK="2" PMFM_FK="82" RANK_ORDER="1" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" ALPHANUMERICAL_VALUE="20-LEUCCIR-001-0003"/>
<!-- poids -->
<!-- <SAMPLE_MEASUREMENT ID="61" SAMPLE_FK="52" RECORDER_DEPARTMENT_FK="2" PMFM_FK="356" RANK_ORDER="2" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="4"/>-->
<!-- taille -->
......@@ -303,6 +309,8 @@
<LANDING_MEASUREMENT ID="32" LANDING_FK="32" PMFM_FK="359" RANK_ORDER="1" ALPHANUMERICAL_VALUE="20-LEUCCIR-001" UPDATE_DATE="2018-03-08" RECORDER_DEPARTMENT_FK="2" QUALITY_FLAG_FK="0"/>
<SAMPLE ID="53" LANDING_FK="32" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" SAMPLE_DATE="2020-12-01 16:00:00.000" CREATION_DATE="2020-12-18" UPDATE_DATE="2018-03-08" LABEL="20-LEUCCIR-001-0004" RANK_ORDER="1" MATRIX_FK="2" QUALITY_FLAG_FK="0" REFERENCE_TAXON_FK="1010" COMMENTS="sample test #53"/>
<!-- tag_id -->
<SAMPLE_MEASUREMENT ID="70" SAMPLE_FK="53" RECORDER_DEPARTMENT_FK="2" PMFM_FK="82" RANK_ORDER="1" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" ALPHANUMERICAL_VALUE="20-LEUCCIR-001-0004"/>
<!-- poids -->
<SAMPLE_MEASUREMENT ID="71" SAMPLE_FK="53" RECORDER_DEPARTMENT_FK="2" PMFM_FK="356" RANK_ORDER="2" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="4"/>
<!-- taille -->
......@@ -320,6 +328,8 @@
<LANDING_MEASUREMENT ID="33" LANDING_FK="33" PMFM_FK="359" RANK_ORDER="1" ALPHANUMERICAL_VALUE="20-LEUCCIR-002" UPDATE_DATE="2018-03-09" RECORDER_DEPARTMENT_FK="2" QUALITY_FLAG_FK="0"/>
<SAMPLE ID="54" LANDING_FK="33" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" SAMPLE_DATE="2020-12-02 16:00:00.000" CREATION_DATE="2020-12-21" UPDATE_DATE="2018-03-08" LABEL="20-LEUCCIR-002-0001" RANK_ORDER="1" MATRIX_FK="2" QUALITY_FLAG_FK="0" REFERENCE_TAXON_FK="1042" COMMENTS="sample test #54"/>
<!-- tag_id -->
<SAMPLE_MEASUREMENT ID="80" SAMPLE_FK="54" RECORDER_DEPARTMENT_FK="2" PMFM_FK="82" RANK_ORDER="1" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" ALPHANUMERICAL_VALUE="20-LEUCCIR-002-0001"/>
<!-- poids -->
<SAMPLE_MEASUREMENT ID="81" SAMPLE_FK="54" RECORDER_DEPARTMENT_FK="2" PMFM_FK="356" RANK_ORDER="2" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="15"/>
<!-- taille -->
......@@ -333,6 +343,8 @@
<LANDING_MEASUREMENT ID="34" LANDING_FK="34" PMFM_FK="359" RANK_ORDER="1" ALPHANUMERICAL_VALUE="20-LEUCCIR-002" UPDATE_DATE="2018-03-09" RECORDER_DEPARTMENT_FK="2" QUALITY_FLAG_FK="0"/>
<SAMPLE ID="55" LANDING_FK="34" PROGRAM_FK="40" RECORDER_DEPARTMENT_FK="2" RECORDER_PERSON_FK="2" SAMPLE_DATE="2020-12-02 16:00:00.000" CREATION_DATE="2020-12-21" UPDATE_DATE="2018-03-08" LABEL="20-LEUCCIR-002-0002" RANK_ORDER="1" MATRIX_FK="2" QUALITY_FLAG_FK="0" REFERENCE_TAXON_FK="1061" COMMENTS="sample test #55"/>
<!-- tag_id -->
<SAMPLE_MEASUREMENT ID="90" SAMPLE_FK="55" RECORDER_DEPARTMENT_FK="2" PMFM_FK="82" RANK_ORDER="1" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" ALPHANUMERICAL_VALUE="20-LEUCCIR-002-0002"/>
<!-- poids -->
<SAMPLE_MEASUREMENT ID="91" SAMPLE_FK="55" RECORDER_DEPARTMENT_FK="2" PMFM_FK="356" RANK_ORDER="2" UPDATE_DATE="2018-03-08" QUALITY_FLAG_FK="0" NUMERICAL_VALUE="28"/>
<!-- taille -->
......
......@@ -96,5 +96,6 @@
<SOFTWARE_PROPERTY ID="113" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.menu.items" NAME="[{&#34;title&#34;: &#34;Lignes de plan&#34;, &#34;path&#34;: &#34;/referential/programs/40/strategies&#34;,&#34;before&#34;:&#34;MENU.OCCASIONS&#34;, &#34;icon&#34;: &#34;contract&#34;, &#34;profile&#34;: &#34;USER&#34;}]"/>
<SOFTWARE_PROPERTY ID="114" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.data.accessNotSelfData.role" NAME="ROLE_USER"/>
<SOFTWARE_PROPERTY ID="115" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.data.accessNotSelfData.department.ids" NAME="3"/>
<SOFTWARE_PROPERTY ID="116" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.persistence.sample.uniqueTag" NAME="true"/>
</dataset>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment