Author: chatellier
Date: 2010-11-30 13:15:21 +0000 (Tue, 30 Nov 2010)
New Revision: 307
Log:
Control des classes de longueur seulement pour les captures de type "poissons"
Modified:
trunk/coser-business/src/main/java/fr/ifremer/coser/CoserBusinessConfig.java
trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java
trunk/coser-business/src/main/resources/fr/ifremer/coser/data/Length-error-validation.xml
trunk/coser-business/src/main/resources/i18n/coser-business_en_GB.properties
trunk/coser-business/src/main/resources/i18n/coser-business_fr_FR.properties
trunk/coser-business/src/test/java/fr/ifremer/coser/services/ControlServiceTest.java
trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/CoserFrameHandler.java
Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/CoserBusinessConfig.java
===================================================================
--- trunk/coser-business/src/main/java/fr/ifremer/coser/CoserBusinessConfig.java 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-business/src/main/java/fr/ifremer/coser/CoserBusinessConfig.java 2010-11-30 13:15:21 UTC (rev 307)
@@ -92,6 +92,11 @@
return result;
}
+ public String getControlTypeFish() {
+ String result = getOption(CoserBusinessOption.CONTROL_TYPE_FISH.key);
+ return result;
+ }
+
public double getSelectionOccurrenceFilter() {
double result = getOptionAsDouble(CoserBusinessOption.SELECTION_FILTER_OCCURRENCE.key);
return result;
@@ -113,6 +118,7 @@
CONTROL_NOBSMIN("coser.control.nobsmin", _("coser.config.control.nobsmin.description"), "1.0", Double.class, false, false),
CONTROL_DIFF_CATCH_LENGTH("coser.control.diffcatchlength", _("coser.config.control.diffcatchlength.description"), "5.0", Double.class, false, false),
+ CONTROL_TYPE_FISH("coser.control.typeFish", _("coser.config.control.typeFish.description"), "Pisces + Agnatha", String.class, false, false),
SELECTION_FILTER_OCCURRENCE("coser.selection.occurrenceFilter", _("coser.config.selection.occurrenceFilter.description"), "5.0", Double.class, false, false),
SELECTION_FILTER_DENSITY("coser.selection.densityFilter", _("coser.config.selection.densityFilter.description"), "5.0", Double.class, false, false);
Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java
===================================================================
--- trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java 2010-11-30 13:15:21 UTC (rev 307)
@@ -37,6 +37,8 @@
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.xwork.StringUtils;
@@ -60,9 +62,9 @@
import fr.ifremer.coser.CoserConstants.ValidationLevel;
import fr.ifremer.coser.bean.Control;
import fr.ifremer.coser.bean.Project;
+import fr.ifremer.coser.control.ControlError;
import fr.ifremer.coser.control.DiffCatchLengthControlError;
import fr.ifremer.coser.control.ProgressMonitor;
-import fr.ifremer.coser.control.ControlError;
import fr.ifremer.coser.data.AbstractDataEntity;
import fr.ifremer.coser.data.Catch;
import fr.ifremer.coser.data.Haul;
@@ -92,7 +94,10 @@
protected ActionValidatorManager validator;
protected ActionContext context;
-
+
+ /** Pattern des tailles de poisson (ex : 1.5, 1.50, 2.00). */
+ protected static final Pattern FISH_LENGTH_PATTERN = Pattern.compile("^\\d+(\\.[05]0*)?$");
+
/**
* Initialise le context xworks.
*
@@ -819,7 +824,7 @@
itHaul.next(); // skip header
// declare all necessary data
- Set<String> refTaxSpecies = new HashSet<String>();
+ Map<String, Integer> refTaxSpecies = new HashMap<String, Integer>();
Set<String> catchYear = new HashSet<String>();
Set<String> lengthYear = new HashSet<String>();
Set<String> haulYear = new HashSet<String>();
@@ -829,14 +834,31 @@
Set<String> haulYearHaul = new HashSet<String>();
Set<String> lengthYearHaul = new HashSet<String>();
Set<String> catchYearHaul = new HashSet<String>();
+ Map<String, Integer[]> specyTypes = new HashMap<String, Integer[]>();
// parcourt de toutes les données
while (itReftax.hasNext() ) {
String[] tuple = itReftax.next();
// "C_Perm","NumSys","NivSys","C_VALIDE","L_VALIDE","AA_VALIDE","C_TxP\u00E8re","Taxa"
- refTaxSpecies.add(tuple[3]);
+ String specyName = tuple[3];
+ Integer iNumSys = Integer.valueOf(tuple[1]);
+ refTaxSpecies.put(specyName, iNumSys);
}
+ // parcourt du fichier des types de données
+ Map<String, Integer[]> mapType = new HashMap<String, Integer[]>();
+ Iterator<String[]> itTypeSpecies = project.getTypeEspeces().iterator();
+ itTypeSpecies.next(); // skip header
+ while (itTypeSpecies.hasNext()) {
+ // "Types";"Commentaire";"NumSys min";"NumSys max"
+ String[] tuple = itTypeSpecies.next();
+ String specyType = tuple[0];
+
+ Integer iMinNumSys = Integer.valueOf(tuple[2]);
+ Integer iMaxNumSys = Integer.valueOf(tuple[3]);
+ mapType.put(specyType, new Integer[]{iMinNumSys, iMaxNumSys});
+ }
+
while (itCatch.hasNext()) {
String[] catchData = itCatch.next();
campagneNames.add(catchData[Catch.INDEX_SURVEY]);
@@ -847,7 +869,7 @@
// Contrôle des noms d'espèces dans fichiers CAPTURES et TAILLES qui
// doivent être dans le référentiel "reftax": CAPTURES$Espece et
// TAILLES$Espece doivent exister dans REFTAX$ C_VALIDE
- if (!refTaxSpecies.contains(catchData[Catch.INDEX_SPECIES])) {
+ if (!refTaxSpecies.containsKey(catchData[Catch.INDEX_SPECIES])) {
ControlError error = new ControlError();
error.setCategory(Category.CATCH);
error.addLineNumber(catchData[Catch.INDEX_LINE]);
@@ -862,19 +884,20 @@
progress.setText(_("coser.business.control.step.crossFileChech", 25));
progress.setCurrent(25);
}
-
+
while (itLength.hasNext()) {
String[] lengthData = itLength.next();
+ String species = lengthData[Length.INDEX_SPECIES];
campagneNames.add(lengthData[Length.INDEX_SURVEY]);
lengthYear.add(lengthData[Length.INDEX_YEAR]);
lengthYearHaulSpecies.add(lengthData[Length.INDEX_YEAR] + "|" +
- lengthData[Length.INDEX_HAUL] + "|" + lengthData[Length.INDEX_SPECIES]);
+ lengthData[Length.INDEX_HAUL] + "|" + species);
lengthYearHaul.add(lengthData[Length.INDEX_YEAR] + "|" + lengthData[Length.INDEX_HAUL]);
-
+
// Contrôle des noms d'espèces dans fichiers CAPTURES et TAILLES qui
// doivent être dans le référentiel "reftax": CAPTURES$Espece et
// TAILLES$Espece doivent exister dans REFTAX$ C_VALIDE
- if (!refTaxSpecies.contains(lengthData[Length.INDEX_SPECIES])) {
+ if (!refTaxSpecies.containsKey(species)) {
ControlError error = new ControlError();
error.setCategory(Category.LENGTH);
error.addLineNumber(lengthData[Length.INDEX_LINE]);
@@ -883,6 +906,40 @@
error.setDetailMessage(_("coser.business.control.error.nonExistantSpeciesDetail", lengthData[Length.INDEX_SPECIES]));
crossFilesErrors.add(error);
}
+
+ else {
+ // ne doit être fait que si l'escepe existe
+
+ // Contrôle sur le champ TAILLES$Longueur :
+ // pour les poissons, vérifier que la première décimale n'est que 0 ou 5
+ // Décision : Warning si différent de 0 ou 5 -> demander la vérification
+ // de l'unité et/ou du pas.
+ // Rappeler que le champ Longueur, quelque soit les espèces, doit être en
+ // cm et inviter l'utilisateur à vérifier toutes ces données de longueur
+
+ Integer speciesNumSys = refTaxSpecies.get(species);
+ for (Map.Entry<String, Integer[]> speciesTypeEntry : specyTypes.entrySet()) {
+ // seulement pour les poissons
+ String speciesType = speciesTypeEntry.getKey();
+ Integer[] bound = speciesTypeEntry.getValue();
+ // si le type courant et le type de la config
+ // et que l'espece courant est de ce type (bornes)
+ // et que la longueur n'est pas valide
+ // alors c'est une erreur
+ if (speciesType.equals(config.getControlTypeFish()) &&
+ speciesNumSys >= bound[0] && speciesNumSys <= bound[1] &&
+ !isValidFishLength(lengthData[Length.INDEX_LENGTH])) {
+ ControlError error = new ControlError();
+ error.setCategory(Category.LENGTH);
+ error.addLineNumber(lengthData[Length.INDEX_LINE]);
+ error.setLevel(ValidationLevel.WARNING);
+ error.setMessage(_("coser.business.control.error.invalidLengthLengthStep"));
+ error.setDetailMessage(_("coser.business.control.error.invalidLengthLengthStepDetail", lengthData[Length.INDEX_LENGTH]));
+ error.setTipMessage(_("coser.business.control.error.invalidLengthLengthStepTip"));
+ crossFilesErrors.add(error);
+ }
+ }
+ }
}
if (progress != null) {
@@ -972,4 +1029,15 @@
return crossFilesErrors;
}
+
+ /**
+ * Return {@code true} if given length is valid length (cm or half cm).
+ *
+ * @param length lengthto test
+ * @return {@code true} if given length is valid length (cm or half cm)
+ */
+ protected boolean isValidFishLength(String length) {
+ Matcher mLength = FISH_LENGTH_PATTERN.matcher(length);
+ return mLength.matches();
+ }
}
Modified: trunk/coser-business/src/main/resources/fr/ifremer/coser/data/Length-error-validation.xml
===================================================================
--- trunk/coser-business/src/main/resources/fr/ifremer/coser/data/Length-error-validation.xml 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-business/src/main/resources/fr/ifremer/coser/data/Length-error-validation.xml 2010-11-30 13:15:21 UTC (rev 307)
@@ -65,10 +65,6 @@
<message>length attribute is not a valid double</message>
</field-validator>
</field>
- <validator type="coserExpression">
- <param name="expression"><![CDATA[lengthAsString.endsWith(".00") || lengthAsString.endsWith(".0") || lengthAsString.endsWith(".50") || lengthAsString.endsWith(".5")]]></param>
- <message>coser.business.control.error.invalidLengthLengthStep</message>
- </validator>
<field name="numberAsString">
<field-validator type="checkDouble">
<param name="notAvailable">NA</param>
Modified: trunk/coser-business/src/main/resources/i18n/coser-business_en_GB.properties
===================================================================
--- trunk/coser-business/src/main/resources/i18n/coser-business_en_GB.properties 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-business/src/main/resources/i18n/coser-business_en_GB.properties 2010-11-30 13:15:21 UTC (rev 307)
@@ -33,6 +33,8 @@
coser.business.control.error.duplicatedLine=Duplicated line
coser.business.control.error.duplicatedLineDetails=Duplicated line for key \: %s
coser.business.control.error.invalidLengthLengthStep=Invalid length step (authorized centimeters and half-centimeters)
+coser.business.control.error.invalidLengthLengthStepDetail=Invalid length step %s
+coser.business.control.error.invalidLengthLengthStepTip=Length field must be in centimeters.
coser.business.control.error.minObservationCount=Minimum number of observation not reached
coser.business.control.error.minObservationCountDetail=Minimum number of observation not reached (%s) \: %.2f
coser.business.control.error.missingYearHaulForCatchData=Missing hauls in haul data for some catch
@@ -60,6 +62,7 @@
coser.business.selection.notValidatedControl=Not validated control \!
coser.config.control.diffcatchlength.description=Percentage difference allowed between catch and length (in percent, for example 5% set 5.0)
coser.config.control.nobsmin.description=Minimal observation number
+coser.config.control.typeFish.description=Fish species code type
coser.config.database.directory.description=Coser database directory (all Coser data)
coser.config.projects.directory.description=Coser projects directory
coser.config.reference.species.description=Species reference file location (reftax)
Modified: trunk/coser-business/src/main/resources/i18n/coser-business_fr_FR.properties
===================================================================
--- trunk/coser-business/src/main/resources/i18n/coser-business_fr_FR.properties 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-business/src/main/resources/i18n/coser-business_fr_FR.properties 2010-11-30 13:15:21 UTC (rev 307)
@@ -32,7 +32,9 @@
coser.business.control.error.diffCatchLengthDetail=Diff\u00E9rence entre les captures et taille pour l'esp\u00E8ce %s (ann\u00E9e %s)
coser.business.control.error.duplicatedLine=Ligne en doublon
coser.business.control.error.duplicatedLineDetails=Ligne en doublon pour la cl\u00E9 \: %s
-coser.business.control.error.invalidLengthLengthStep=Pas de longeur invalide (autoris\u00E9 centim\u00E8tre et demi-centim\u00E8tre)
+coser.business.control.error.invalidLengthLengthStep=Pas de longueur invalide (autoris\u00E9 centim\u00E8tre et demi-centim\u00E8tre)
+coser.business.control.error.invalidLengthLengthStepDetail=Pas longueur %s invalide
+coser.business.control.error.invalidLengthLengthStepTip=Le champs de longueur doit \u00EAtre en centim\u00E8tre (ou demi centim\u00E8tre)
coser.business.control.error.minObservationCount=Nombre minimal d'observation non atteint
coser.business.control.error.minObservationCountDetail=Nombre minimal d'observation non atteint (%s) \: %.2f
coser.business.control.error.missingYearHaulForCatchData=Traits manquants dans les traits pour certaines captures
@@ -60,6 +62,7 @@
coser.business.selection.notValidatedControl=Contr\u00F4le non valid\u00E9 \!
coser.config.control.diffcatchlength.description=Pourcentage d'\u00E9cart tol\u00E9r\u00E9 entre les captures et les tailles (en pourcent, par exemple pour 5% mettre 5.0)
coser.config.control.nobsmin.description=Nombre minimal d'observation
+coser.config.control.typeFish.description=Nom du type d'esp\u00E8ces correspondant aux poissons
coser.config.database.directory.description=Emplacement du dossier de toutes les donn\u00E9es relatives \u00E0 Coser
coser.config.projects.directory.description=Emplacement du dossier de sauvegarde des projets
coser.config.reference.species.description=Emplacement du fichier de r\u00E9f\u00E9rence des esp\u00E8ces (Reftax)
Modified: trunk/coser-business/src/test/java/fr/ifremer/coser/services/ControlServiceTest.java
===================================================================
--- trunk/coser-business/src/test/java/fr/ifremer/coser/services/ControlServiceTest.java 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-business/src/test/java/fr/ifremer/coser/services/ControlServiceTest.java 2010-11-30 13:15:21 UTC (rev 307)
@@ -54,7 +54,7 @@
private static final Log log = LogFactory.getLog(ControlServiceTest.class);
- protected ControlService validationService = new ControlService(config);
+ protected ControlService controlService = new ControlService(config);
/**
* Test les validations sur les champs vide.
@@ -65,12 +65,12 @@
Catch myCatch = new Catch();
myCatch.setData(new String[]{"1", "","","","","",""});
- List<ControlError> errors = validationService.validate(myCatch, Category.CATCH);
+ List<ControlError> errors = controlService.validate(myCatch, Category.CATCH);
Assert.assertNotNull(errors);
Assert.assertEquals(4, errors.size());
myCatch.setData(new String[]{"1", "Toto","","","","999",""});
- errors = validationService.validate(myCatch, Category.CATCH);
+ errors = controlService.validate(myCatch, Category.CATCH);
Assert.assertNotNull(errors);
Assert.assertEquals(3, errors.size());
}
@@ -82,7 +82,7 @@
public void testDoubleValidation() {
Catch myCatch = new Catch();
myCatch.setData(new String[]{"1", "Test survey","1999","Test trait","Test sp","NA","12"});
- List<ControlError> errors = validationService.validate(myCatch, Category.CATCH);
+ List<ControlError> errors = controlService.validate(myCatch, Category.CATCH);
log.warn(errors);
Assert.assertTrue(errors.isEmpty());
}
@@ -95,9 +95,10 @@
Length myLength = new Length();
// "Survey","Year","Haul","Species","Sex","Maturity","Length","Number","Weight","Age"
myLength.setData(new String[]{"1", "Test survey","1999","Test trait","Test sp","i","m","23.25", "3.00", "44.99", "12"});
- List<ControlError> errors = validationService.validate(myLength, Category.LENGTH);
+ List<ControlError> errors = controlService.validate(myLength, Category.LENGTH);
log.warn(errors);
- Assert.assertEquals(1, errors.size());
+ // TODO test a refaire (croisement de fichier)
+ //Assert.assertEquals(1, errors.size());
}
/**
@@ -107,16 +108,16 @@
public void test5DecimalValidation() {
Haul haulBean = new Haul();
haulBean.setData(new String[]{"1", "COSER_TEST","2010","TRAIT3","10","STR3","0.06","43.89","1.73","115.00"});
- List<ControlError> errors = validationService.validate(haulBean, Category.HAUL);
+ List<ControlError> errors = controlService.validate(haulBean, Category.HAUL);
Assert.assertEquals(3, errors.size());
haulBean.setData(new String[]{"1","COSER_TEST","2010","TRAIT3","10","STR3","0.06000","43.89000","1.73234","115.00"});
- errors = validationService.validate(haulBean, Category.HAUL);
+ errors = controlService.validate(haulBean, Category.HAUL);
Assert.assertTrue(errors.isEmpty());
}
/**
- * Test, si un poids est reseigné, la capture doit l'etre.
+ * Test, si un poids est renseigné, la capture doit l'etre.
*/
@Test
public void testWeigthWithNonCapture() {
@@ -125,10 +126,27 @@
dataCatch.add(new String[]{"Line", "Campagne","Annee","Trait","Espece","Nombre","Poids"});
dataCatch.add(new String[]{"1", "Test survey","1999","Test trait","Test sp","0","12"});
control.setCatch(dataCatch);
- List<ControlError> errors = validationService.validateCategory(control, Category.CATCH, null);
+ List<ControlError> errors = controlService.validateCategory(control, Category.CATCH, null);
if (log.isDebugEnabled()) {
log.debug("Validation errors = " + errors);
}
Assert.assertEquals(2, errors.size());
}
+
+ /**
+ * Test si le pas de longueur est valid pour les poissons.
+ */
+ @Test
+ public void testValidFishLength() {
+ Assert.assertTrue(controlService.isValidFishLength("4"));
+ Assert.assertTrue(controlService.isValidFishLength("4.5"));
+ Assert.assertTrue(controlService.isValidFishLength("0"));
+ Assert.assertTrue(controlService.isValidFishLength("0.5"));
+ Assert.assertTrue(controlService.isValidFishLength("1.50"));
+ Assert.assertTrue(controlService.isValidFishLength("1.00"));
+ Assert.assertTrue(controlService.isValidFishLength("1.500"));
+ Assert.assertFalse(controlService.isValidFishLength(""));
+ Assert.assertFalse(controlService.isValidFishLength("1.3"));
+ Assert.assertFalse(controlService.isValidFishLength("1.001"));
+ }
}
Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/CoserFrameHandler.java
===================================================================
--- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/CoserFrameHandler.java 2010-11-30 09:56:42 UTC (rev 306)
+++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/CoserFrameHandler.java 2010-11-30 13:15:21 UTC (rev 307)
@@ -161,6 +161,7 @@
modelBuilder.addCategory(_("coser.config.category.configuration"), _("coser.config.category.configuration.description"));
modelBuilder.addOption(CoserBusinessConfig.CoserBusinessOption.CONTROL_NOBSMIN);
modelBuilder.addOption(CoserBusinessConfig.CoserBusinessOption.CONTROL_DIFF_CATCH_LENGTH);
+ modelBuilder.addOption(CoserBusinessConfig.CoserBusinessOption.CONTROL_TYPE_FISH);
modelBuilder.addOption(CoserBusinessConfig.CoserBusinessOption.SELECTION_FILTER_OCCURRENCE);
modelBuilder.addOption(CoserBusinessConfig.CoserBusinessOption.SELECTION_FILTER_DENSITY);