From 990aae3ae96cb35206d9d1161adabbe6b4d3b135 Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Wed, 14 Aug 2024 17:02:23 +0200 Subject: [PATCH 01/15] =?UTF-8?q?Ajouter=20une=20m=C3=A9thode=20pour=20cal?= =?UTF-8?q?culer=20toutes=20les=20valeurs=20correspondant=20au=20jeu=20cli?= =?UTF-8?q?matique=20fourni.=20refs=20agroclim/agrometinfo/AgroMetInfo=5F2?= =?UTF-8?q?.0#35?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agroclim/indicators/model/Evaluation.java | 163 ++++++++++-------- .../model/indicator/CompositeIndicator.java | 10 +- .../indicators/model/indicator/Indicator.java | 14 +- .../model/EvaluationHourlyTest.java | 12 +- .../model/EvaluationRobertTest.java | 12 +- .../indicators/model/EvaluationTest.java | 51 ++++-- .../model/EvalutationCustomHeadersTest.java | 12 +- .../indicators/model/MMarjouTest.java | 14 +- .../indicators/model/RaidayMeantTest.java | 45 +++-- 9 files changed, 188 insertions(+), 145 deletions(-) diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java index 8e78e9e4..31baba23 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java @@ -16,8 +16,6 @@ */ package fr.inrae.agroclim.indicators.model; -import java.io.IOException; -import java.io.ObjectInputStream; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; @@ -106,21 +104,6 @@ public final class Evaluation extends CompositeIndicator { }); } - /** - * Computed phases (or phases from file) used to compute indicators. - * - * TODO : supprimer et utiliser - * EvaluationResult::getPhaseResults::getAnnualPhase. - */ - @Getter - private transient List<AnnualPhase> computedPhases; - - /** - * Results of computation by year. - */ - @Getter - private transient Map<Integer, EvaluationResult> results = new LinkedHashMap<>(); - /** * All resources needed to run the evaluation. * @@ -129,6 +112,11 @@ public final class Evaluation extends CompositeIndicator { @Getter private final ResourceManager resourceManager; + /** + * Flag to ignore when climatic data is empty for a phase. + */ + private boolean ignoreEmptyClimaticData = false; + /** * Flag for state Saved. */ @@ -198,12 +186,7 @@ public final class Evaluation extends CompositeIndicator { throw new RuntimeException("This should never occur!", ex); } } - if (evaluation.computedPhases != null) { - computedPhases = new ArrayList<>(); - computedPhases.addAll(evaluation.computedPhases); - } isTranscient = evaluation.isTranscient; - results = evaluation.results; state = evaluation.state; } @@ -285,11 +268,26 @@ public final class Evaluation extends CompositeIndicator { } /** - * Clear results of indicators. + * Check data before running compute* methods. + * + * @param phases phenological phases to check + * @param climaticResource climatic resource to check + * @throws IndicatorsException exception */ - private void clearResults() { - results.clear(); - computedPhases = new ArrayList<>(); + private void checkBeforeCompute(final List<CompositeIndicator> phases, final ClimaticResource climaticResource) + throws IndicatorsException { + if (phases == null) { + throw new RuntimeException("Phase list is null!"); + } + if (phases.isEmpty()) { + throw new RuntimeException("Phase list is empty!"); + } + if (climaticResource.isEmpty()) { + throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY); + } + if (resourceManager.getPhenologicalResource().isEmpty()) { + throw new IndicatorsException(ResourceErrorType.PHENO_EMPTY); + } } @Override @@ -300,32 +298,30 @@ public final class Evaluation extends CompositeIndicator { /** * Compute indicator results. * + * @return Results of computation by year. * @throws IndicatorsException * from Indicator.compute() */ - public void compute() throws IndicatorsException { + public Map<Integer, EvaluationResult> compute() throws IndicatorsException { LOGGER.trace("start computing evaluation \"" + getName() + "\""); + this.ignoreEmptyClimaticData = false; fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_START.event(this)); final List<CompositeIndicator> phases = getPhases(); - if (phases == null) { - throw new RuntimeException("Phase list is null!"); - } - if (phases.isEmpty()) { - throw new RuntimeException("Phase list is empty!"); - } - if (resourceManager.getClimaticResource().isEmpty()) { - throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY); - } - if (resourceManager.getPhenologicalResource().isEmpty()) { - throw new IndicatorsException(ResourceErrorType.PHENO_EMPTY); - } + final ClimaticResource climaticResource = resourceManager.getClimaticResource(); + checkBeforeCompute(phases, climaticResource); + + var results = compute(climaticResource, phases); + fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_SUCCESS.event(this)); + return results; + } - clearResults(); + private Map<Integer, EvaluationResult> compute(final ClimaticResource climaticResource, + final List<CompositeIndicator> phases) throws IndicatorsException { + final Map<Integer, EvaluationResult> results = new LinkedHashMap<>(); /* Pour chaque phase */ - List<AnnualStageData> stageDatas; - stageDatas = getResourceManager().getPhenologicalResource().getData(); + final List<AnnualStageData> stageDatas = getResourceManager().getPhenologicalResource().getData(); for (final CompositeIndicator phase : phases) { final String phaseId = phase.getId(); if (phaseId == null) { @@ -348,8 +344,7 @@ public final class Evaluation extends CompositeIndicator { dateYear -= 1; } - if (!resourceManager.getClimaticResource().getYears() - .contains(dateYear)) { + if (!climaticResource.getYears().contains(dateYear)) { continue; } @@ -368,7 +363,6 @@ public final class Evaluation extends CompositeIndicator { annualPhase.setEndStage(endStageName); annualPhase.setStartStage(startStageName); annualPhase.setUid(phaseId); - computedPhases.add(annualPhase); final PhaseResult phaseResult = new PhaseResult(); phaseResult.setAnnualPhase(annualPhase); results.get(year).getPhaseResults().add(phaseResult); @@ -399,15 +393,19 @@ public final class Evaluation extends CompositeIndicator { } /* Données climatiques pendant la phase et l'année donnée */ - final ClimaticResource climaticData = resourceManager - .getClimaticResource().getClimaticDataByPhaseAndYear(startDate, endDate); + final ClimaticResource climaticData = climaticResource + .getClimaticDataByPhaseAndYear(startDate, endDate); if (climaticData.isEmpty()) { - if (resourceManager.getClimaticResource().isEmpty()) { + if (climaticResource.isEmpty()) { throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY); } + if (ignoreEmptyClimaticData) { + continue; + } final int yearToSearch = dateYear; - final List<ClimaticDailyData> ddataList = resourceManager.getClimaticResource().getData() - .stream().filter(f -> f.getYear() == yearToSearch).collect(Collectors.toList()); + final List<ClimaticDailyData> ddataList = climaticResource.getData().stream() // + .filter(f -> f.getYear() == yearToSearch) // + .collect(Collectors.toList()); final ClimaticDailyData startData = ddataList.get(0); final ClimaticDailyData endData = ddataList.get(ddataList.size() - 1); @@ -433,9 +431,43 @@ public final class Evaluation extends CompositeIndicator { } } - computeFaisability(); - fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_SUCCESS.event(this)); + computeFaisability(results); LOGGER.trace("end of computing evaluation \"{}\"", getName()); + return results; + } + + /** + * Compute indicator results for each step of provided climatic data. + * + * @return Results of computation by year, for each step in climatic data. + * @throws IndicatorsException + * from Indicator.compute() + */ + public Map<Date, Map<Integer, EvaluationResult>> computeEach() throws IndicatorsException { + LOGGER.info("Start"); + this.ignoreEmptyClimaticData = true; + final List<CompositeIndicator> phases = getPhases(); + final ClimaticResource climaticResource = resourceManager.getClimaticResource(); + checkBeforeCompute(phases, climaticResource); + + final List<ClimaticDailyData> dailyData = climaticResource.getData(); + // first, create Maps + final Map<Date, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); + //dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); + // then compute + final ClimaticResource resource = new ClimaticResource(); + resource.setMissingVariables(climaticResource.getMissingVariables()); + for (int i = 0; i < dailyData.size(); i++) { + final Date date = dailyData.get(i).getDate(); + LOGGER.info("Compute {}", date); + final List<ClimaticDailyData> data = dailyData.subList(0, i); + resource.setData(data); + var results = compute(resource, phases); + LOGGER.info("Results : {}", results); + allResults.put(date, results); + } + LOGGER.info("End"); + return allResults; } /** @@ -448,9 +480,10 @@ public final class Evaluation extends CompositeIndicator { * Pour chaque année, Pour chaque phase, valeurs.onIndicatorAdd(valeur phase * p, année n); Fin pour aggregation(valeurs); Fin pour; * + * @param results Results of computation by year. * @throws IndicatorsException raised by AggregationFunction.aggregate() */ - private void computeFaisability() throws IndicatorsException { + private void computeFaisability(final Map<Integer, EvaluationResult> results) throws IndicatorsException { LOGGER.traceEntry(); if (getType() == EvaluationType.WITHOUT_AGGREGATION) { return; @@ -459,18 +492,14 @@ public final class Evaluation extends CompositeIndicator { // if only 1 phase, no need to aggregate // evaluation value = value of the phase results.values().forEach(result -> - result.setNormalizedValue( - result.getPhaseResults().get(0).getNormalizedValue() - ) - ); + result.setNormalizedValue(result.getPhaseResults().get(0).getNormalizedValue())); return; } else if (getAggregationFunction().getExpression() == null) { throw new IllegalStateException("An evaluation with more than 1 " + "phase must have a defined expression for aggregation!"); } - for (final Map.Entry<Integer, EvaluationResult> entry - : results.entrySet()) { + for (final Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) { final EvaluationResult evaluationResult = entry.getValue(); final int year = entry.getKey(); if (evaluationResult == null) { @@ -872,22 +901,6 @@ public final class Evaluation extends CompositeIndicator { return result; } - /** - * Context: Deserialization does not initialize results. - * - * A final field must be initialized either by direct assignment of an initial value or in the constructor. During - * deserialization, neither of these are invoked, so initial values for transients must be set in the - * 'readObject()' private method that's invoked during deserialization. And for that to work, the transients must - * be non-final. - * - * @param ois input stream from deserialization - */ - private void readObject(final ObjectInputStream ois) throws ClassNotFoundException, IOException { - // perform the default de-serialization first - ois.defaultReadObject(); - results = new HashMap<>(); - } - /** * Set parameters (id and attributes) for the indicator and its criteria * from knowledge defined in settings. diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java index c0af4e5e..e0b76d50 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java @@ -252,14 +252,14 @@ implements DataLoadingListener, Detailable, HasDataLoadingListener, Comparable<I } @Override - public final Double compute(final Resource<? extends DailyData> climRessource) throws IndicatorsException { - if (climRessource.getYears().isEmpty()) { + public final Double compute(final Resource<? extends DailyData> climResource) throws IndicatorsException { + if (climResource.getYears().isEmpty()) { throw new RuntimeException( String.format( "No years in ClimaticResource (%d dailyData)!", - climRessource.getData().size())); + climResource.getData().size())); } - final HashMap<String, Double> results = new HashMap<>(); + final Map<String, Double> results = new HashMap<>(); double valueAfterAggregation = 0; Double valueAfterNormalization; @@ -274,7 +274,7 @@ implements DataLoadingListener, Detailable, HasDataLoadingListener, Comparable<I } try { - final double value = indicator.compute(climRessource); + final Double value = indicator.compute(climResource); results.put(indicator.getId(), value); } catch (final IndicatorsException e) { throw new IndicatorsException(ComputationErrorType.COMPOSITE_COMPUTATION, e, indicator.getId()); diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java index 22969b6c..e383f79e 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java @@ -186,6 +186,7 @@ Serializable, UseVariables { * Normalized value. */ @XmlTransient + @Getter @Setter private Double value; @@ -225,8 +226,8 @@ Serializable, UseVariables { } } listeners.add(IndicatorListener.class, listener); - if (this instanceof CompositeIndicator) { - ((CompositeIndicator) this).getIndicators() + if (this instanceof CompositeIndicator compositeIndicator) { + compositeIndicator.getIndicators() .forEach(i -> i.addIndicatorListener(listener)); } } @@ -364,15 +365,6 @@ Serializable, UseVariables { */ public abstract EvaluationType getType(); - /** - * The normalized value. - * - * @return Normalized value - */ - public final Double getValue() { - return value; - } - /** * @param indicatorCategory indicator category */ diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java index f90f64f0..0de0bdb9 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java @@ -2,8 +2,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.util.List; @@ -52,16 +52,16 @@ public class EvaluationHourlyTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); assertNotNull(results); assertEquals(1, results.keySet().size()); assertTrue(results.containsKey(2015)); diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java index 8e99a6bc..38d60322 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java @@ -20,8 +20,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.util.Arrays; @@ -80,16 +80,16 @@ public class EvaluationRobertTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); assertNotNull(results); assertEquals(2, results.keySet().size()); assertTrue(results.containsKey(1991)); diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java index a3f975d4..85bf316e 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.io.FileOutputStream; @@ -49,6 +50,7 @@ import fr.inrae.agroclim.indicators.model.indicator.Indicator; import fr.inrae.agroclim.indicators.model.result.EvaluationResult; import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; +import java.util.Date; import lombok.NonNull; import lombok.extern.log4j.Log4j2; @@ -128,6 +130,32 @@ public final class EvaluationTest extends DataTestHelper { assertNull(error, error); } + /** + * Check if evaluation computes. + */ + @Test + public void computeEach() { + assertTrue("Evaluation must not be null!", evaluation != null); + final Map<Date, Map<Integer, EvaluationResult>> results; + try { + evaluation.initializeResources(); + results = evaluation.computeEach(); + } catch (final IndicatorsException e) { + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; + } + assertNotNull(results); + + assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); + final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); + assertNotNull(years); + System.out.println(years); + assertEquals(1, years.size()); + final Integer expectedYear = 2015; + assertEquals(expectedYear, years.get(0)); + } + @Test public void computableWithNoData() { final File xmlFile = getEvaluationWithNoDataTestFile(); @@ -184,8 +212,11 @@ public final class EvaluationTest extends DataTestHelper { String error = null; try { evaluation.initializeResources(); - evaluation.compute(); - final List<AnnualPhase> phases = evaluation.getComputedPhases(); + final Map<Integer, EvaluationResult> results = evaluation.compute(); + final List<AnnualPhase> phases = results.values().stream() // + .flatMap(r -> r.getPhaseResults().stream()) // + .map(r -> r.getAnnualPhase()) // + .toList(); final int nbOfPhases = evaluation.getIndicators().size(); final int nbOfYears = evaluation.getClimaticResource().getYears().size(); assertEquals(nbOfPhases * nbOfYears, phases.size()); @@ -238,8 +269,7 @@ public final class EvaluationTest extends DataTestHelper { final String fmt = "%s | %4d | %s-%s | %f | %f"; try { evaluation.initializeResources(); - evaluation.compute(); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); + final Map<Integer, EvaluationResult> results = evaluation.compute(); assertNotNull("Results must not be null!", results); results.entrySet().forEach((entryER) -> { final Integer year = entryER.getKey(); @@ -420,15 +450,16 @@ public final class EvaluationTest extends DataTestHelper { public void setParameters() { assertTrue("Evaluation must not be null!", evaluation != null); String error = null; + Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); final Double initValue = getResultValue("s0s1", "heat", 2015, results); assertNotNull("value of climatic effect \"heat\" must not be null", initValue); @@ -438,11 +469,11 @@ public final class EvaluationTest extends DataTestHelper { parameters.put("Theat", 25.); evaluation.setParametersValues(parameters); try { - evaluation.compute(); + results = evaluation.compute(); } catch (IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); final Double newValue = getResultValue("s0s1", "heat", 2015, results); assertNotEquals("init = " + initValue + ", new = " + newValue, initValue, newValue); diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java index d226c65e..d02a249d 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java @@ -20,8 +20,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.util.Map; @@ -63,15 +63,15 @@ public class EvalutationCustomHeadersTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); assertNotNull("Results must not be null!", results); assertEquals("One year en climate file", 1, results.keySet().size()); results.values().stream().flatMap(v -> v.getPhaseResults().stream()).forEach(phaseResult -> { diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java index 82613882..a102b23a 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java @@ -17,7 +17,6 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.File; @@ -35,6 +34,7 @@ import fr.inrae.agroclim.indicators.model.result.EvaluationResult; import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; import lombok.extern.log4j.Log4j2; +import static org.junit.Assert.fail; /** * Test that Marine Marjou's Getari file computes. @@ -76,16 +76,17 @@ public class MMarjouTest extends DataTestHelper { @Test public void raidaysInfNbDaysInPhase() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); long start = System.currentTimeMillis(); - evaluation.compute(); + results = evaluation.compute(); LOGGER.info("Evaluation.compute() last {} seconds", (System.currentTimeMillis() - start) / 1000.); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); // extract stages to compute number of days Map<Integer, Map<String, Integer>> stageDates = new HashMap<>(); List<AnnualStageData> aSDatas; @@ -100,9 +101,8 @@ public class MMarjouTest extends DataTestHelper { }); }); // check excraidays, hraidays and raidays - assertFalse(evaluation.getResults().isEmpty()); + assertFalse(results.isEmpty()); String fmt = "%d | %s %s : %d | %s=%.4f"; - Map<Integer, EvaluationResult> results = evaluation.getResults(); for (Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) { Integer year = entry.getKey(); if (!stageDates.containsKey(year)) { diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java index c9c8ba33..c31942d7 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java @@ -19,8 +19,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; @@ -208,8 +208,8 @@ public class RaidayMeantTest extends DataTestHelper { @Test public void computeUsingPhenologyCalculator() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; - List<AnnualStageData> annualStageDatas = null; + final List<AnnualStageData> annualStageDatas; + final Map<Integer, EvaluationResult> results; try { final EvaluationSettings settings = evaluation.getSettings(); // wheat Soissons @@ -221,25 +221,29 @@ public class RaidayMeantTest extends DataTestHelper { settings.getPhenologyLoader().setCalculator(calc); settings.getPhenologyLoader().setFile(null); evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); annualStageDatas = evaluation.getResourceManager().getPhenologicalResource().getData(); } catch (final IndicatorsException | IOException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); // first year in phenology file final int firstYear = 1959; assertNotNull(annualStageDatas); assertFalse(annualStageDatas.isEmpty()); assertTrue(firstYear == annualStageDatas.get(0).getYear()); - final List<AnnualPhase> computedPhases = evaluation.getComputedPhases(); + final List<AnnualPhase> computedPhases = results.values().stream() // + .flatMap(r -> r.getPhaseResults().stream()) // + .map(r -> r.getAnnualPhase()) // + .toList(); assertNotNull(computedPhases); assertFalse(computedPhases.isEmpty()); assertTrue(firstYear == computedPhases.get(0).getHarvestYear()); compareStagesAndPhases(annualStageDatas, computedPhases); - assertFalse(evaluation.getResults().isEmpty()); + assertFalse(results.isEmpty()); } /** @@ -248,14 +252,15 @@ public class RaidayMeantTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); // extract stages to compute number of days final Map<Integer, Map<String, Integer>> stageDates = new HashMap<>(); List<AnnualStageData> aSDatas; @@ -271,9 +276,9 @@ public class RaidayMeantTest extends DataTestHelper { }); // check excraidays, hraidays and raidays - assertFalse(evaluation.getResults().isEmpty()); + assertFalse(results.isEmpty()); final String fmt = "%d | %s %s : %d | %s=%.4f"; - for (final Map.Entry<Integer, EvaluationResult> entry : evaluation.getResults().entrySet()) { + for (final Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) { final Integer year = entry.getKey(); if (!stageDates.containsKey(year)) { LOGGER.trace("No stage date for year {}", year); @@ -317,14 +322,16 @@ public class RaidayMeantTest extends DataTestHelper { } assertNotNull(aSDatas); - final List<AnnualPhase> computedPhases = evaluation.getComputedPhases(); + final List<AnnualPhase> computedPhases = results.values().stream() // + .flatMap(r -> r.getPhaseResults().stream()) // + .map(r -> r.getAnnualPhase()) // + .toList(); compareStagesAndPhases(aSDatas, computedPhases); - Map<String, Map<Integer, Map<String, Double>>> computation; - computation = new HashMap<>(); + final Map<String, Map<Integer, Map<String, Double>>> computation = new HashMap<>(); - evaluation.getResults().forEach((year, results) -> { - results.getPhaseResults().forEach((phase) -> { + results.forEach((year, r) -> { + r.getPhaseResults().forEach((phase) -> { final String phaseId = phase.getPhaseId(); if (!computation.containsKey(phaseId)) { computation.put(phaseId, new HashMap<>()); -- GitLab From 2185516a0f8c821fef9c79bf90923e68e710eb5e Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 14:23:29 +0200 Subject: [PATCH 02/15] Ajouter des tests --- .../agroclim/indicators/model/Evaluation.java | 14 +- .../model/result/EvaluationResult.java | 2 + .../model/EvaluationEachDateTest.java | 134 ++++++++++++++++++ .../indicators/model/EvaluationTest.java | 27 ---- .../indicators/xml/evaluation-phalen.gri | 100 +++++++++++++ 5 files changed, 242 insertions(+), 35 deletions(-) create mode 100644 src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java create mode 100644 src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java index 31baba23..8dceebc1 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import fr.inrae.agroclim.indicators.exception.IndicatorsException; import fr.inrae.agroclim.indicators.exception.type.ResourceErrorType; +import fr.inrae.agroclim.indicators.model.data.DailyData; import fr.inrae.agroclim.indicators.model.data.ResourceManager; import fr.inrae.agroclim.indicators.model.data.Variable; import fr.inrae.agroclim.indicators.model.data.Variable.Type; @@ -439,12 +440,12 @@ public final class Evaluation extends CompositeIndicator { /** * Compute indicator results for each step of provided climatic data. * - * @return Results of computation by year, for each step in climatic data. + * @return Results of computation by year, for each step in climatic data: + * Climatic date ⮕ (year, {@link EvalutationResult}). * @throws IndicatorsException * from Indicator.compute() */ - public Map<Date, Map<Integer, EvaluationResult>> computeEach() throws IndicatorsException { - LOGGER.info("Start"); + public Map<Date, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException { this.ignoreEmptyClimaticData = true; final List<CompositeIndicator> phases = getPhases(); final ClimaticResource climaticResource = resourceManager.getClimaticResource(); @@ -453,20 +454,17 @@ public final class Evaluation extends CompositeIndicator { final List<ClimaticDailyData> dailyData = climaticResource.getData(); // first, create Maps final Map<Date, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); - //dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); + dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); // then compute final ClimaticResource resource = new ClimaticResource(); resource.setMissingVariables(climaticResource.getMissingVariables()); for (int i = 0; i < dailyData.size(); i++) { final Date date = dailyData.get(i).getDate(); - LOGGER.info("Compute {}", date); final List<ClimaticDailyData> data = dailyData.subList(0, i); resource.setData(data); - var results = compute(resource, phases); - LOGGER.info("Results : {}", results); + final Map<Integer, EvaluationResult> results = compute(resource, phases); allResults.put(date, results); } - LOGGER.info("End"); return allResults; } diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java b/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java index 70782bf1..d0fba97c 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import lombok.Getter; +import lombok.ToString; /** * Normalized value of an evaluation for a year and values of composed phases. @@ -30,6 +31,7 @@ import lombok.Getter; * @author $Author$ * @version $Revision$ */ +@ToString public class EvaluationResult extends Result { /** diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java new file mode 100644 index 00000000..5b8c6daa --- /dev/null +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java @@ -0,0 +1,134 @@ +package fr.inrae.agroclim.indicators.model; + +import fr.inrae.agroclim.indicators.exception.IndicatorsException; +import fr.inrae.agroclim.indicators.model.data.DataTestHelper; +import fr.inrae.agroclim.indicators.model.indicator.CompositeIndicator; +import fr.inrae.agroclim.indicators.model.indicator.Indicator; +import fr.inrae.agroclim.indicators.model.result.EvaluationResult; +import fr.inrae.agroclim.indicators.model.result.IndicatorResult; +import fr.inrae.agroclim.indicators.model.result.PhaseResult; +import fr.inrae.agroclim.indicators.util.DateUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; + +/** + * Test {@link Evaluation#computeEachDate()}. + * + * @author Olivier Maury + */ +public class EvaluationEachDateTest extends DataTestHelper { + + /** + * Evaluation from good XML file. + */ + private Evaluation evaluation; + + /** + * Set up Evaluation to test. + */ + @Before + public void beforeTest() { + final File xmlFile = getFile("xml/evaluation-phalen.gri"); + evaluation = getEvaluation(xmlFile); + } + + /** + * Check if evaluation computes. + */ + @Test + public void computeEachDate() { + assertTrue("Evaluation must not be null!", evaluation != null); + final Map<Date, Map<Integer, EvaluationResult>> results; + try { + evaluation.initializeResources(); + results = evaluation.computeEachDate(); + } catch (final IndicatorsException e) { + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; + } + assertNotNull(results); + + assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); + final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); + assertNotNull(years); + assertEquals(1, years.size()); + final Integer expectedYear = 2015; + assertEquals(expectedYear, years.get(0)); + + // s0s1 : phalen + final List<Indicator> indicators = new ArrayList<>(); + final List<Indicator> allIndicators = new ArrayList<>(); + allIndicators.addAll(evaluation.getIndicators()); + Indicator indicator = allIndicators.remove(0); + while (indicator != null) { + if (indicator instanceof CompositeIndicator c) { + allIndicators.addAll(c.getIndicators()); + } else { + indicators.add(indicator); + } + if (!allIndicators.isEmpty()) { + indicator = allIndicators.remove(0); + } else { + indicator = null; + } + } + final int nbOfIndicators = indicators.size(); + assertEquals(1, nbOfIndicators); + + // no data on 1st january 2015 as no phase + final Optional<Date> firstDateOptional = results.keySet().stream().findFirst(); + assertTrue(firstDateOptional.isPresent()); + final Date firstDate = firstDateOptional.get(); + final Date expectedFirstDate = DateUtils.getDate(expectedYear, 1); + assertEquals(expectedFirstDate, firstDate); + assertFalse(results.get(firstDate).containsKey(expectedYear)); + + // s0s1 : 120 − 140 + final int s0 = 120; + final int s0Plus1 = s0 + 1; + final int s1 = 140; + List.of(s0Plus1, s1).forEach(doy -> { + final Date date = DateUtils.getDate(expectedYear, doy); + final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + assertFalse(phaseResults.isEmpty()); + assertEquals(1, phaseResults.size()); + final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); + assertNotNull("On " + date + "/" + doy +", process results must not be null!", processResults); + assertEquals(1, processResults.size()); + assertEquals("growth", processResults.get(0).getIndicatorId()); + + // practices > growth > phalength > phalen + final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); + assertEquals("phalength", practicesResults.get(0).getIndicatorId()); + List<IndicatorResult> indicatorResults = practicesResults.get(0).getIndicatorResults(); + assertEquals("phalen", indicatorResults.get(0).getIndicatorId()); + Double value = indicatorResults.get(0).getRawValue(); + assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); + }); + + // Check values + for (int doy = s0Plus1; doy < s1; doy++) { + final Date date = DateUtils.getDate(expectedYear, doy); + final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); + final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); + final List<IndicatorResult> indicatorResults = practicesResults.get(0).getIndicatorResults(); + final Double value = indicatorResults.get(0).getRawValue(); + assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); + final Double expected = Double.valueOf(doy - s0); + assertEquals("On " + date + "/" + doy + ", phalen must equal " + expected, expected, value); + } + } +} diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java index 85bf316e..0603a053 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java @@ -50,7 +50,6 @@ import fr.inrae.agroclim.indicators.model.indicator.Indicator; import fr.inrae.agroclim.indicators.model.result.EvaluationResult; import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; -import java.util.Date; import lombok.NonNull; import lombok.extern.log4j.Log4j2; @@ -130,32 +129,6 @@ public final class EvaluationTest extends DataTestHelper { assertNull(error, error); } - /** - * Check if evaluation computes. - */ - @Test - public void computeEach() { - assertTrue("Evaluation must not be null!", evaluation != null); - final Map<Date, Map<Integer, EvaluationResult>> results; - try { - evaluation.initializeResources(); - results = evaluation.computeEach(); - } catch (final IndicatorsException e) { - final String error = e.getClass() + " : " + e.getLocalizedMessage(); - fail(error); - return; - } - assertNotNull(results); - - assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); - final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); - assertNotNull(years); - System.out.println(years); - assertEquals(1, years.size()); - final Integer expectedYear = 2015; - assertEquals(expectedYear, years.get(0)); - } - @Test public void computableWithNoData() { final File xmlFile = getEvaluationWithNoDataTestFile(); diff --git a/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri new file mode 100644 index 00000000..5d756803 --- /dev/null +++ b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!DOCTYPE evaluationSettings PUBLIC + "-//INRAE AgroClim.//DTD Evaluation 1.1//EN" + "https://agroclim.inrae.fr/getari/dtd/1.1/evaluation.dtd"> +<evaluationSettings timescale="DAILY" timestamp="2024-08-19T13:43:21.127972376" version="1.2.2+20230809135003"> + <climate> + <file path="../model/data/climate/climate-2015.csv"> + <separator> </separator> + <header></header> + <header>year</header> + <header>month</header> + <header>day</header> + <header>tmin</header> + <header>tmax</header> + <header>tmean</header> + <header></header> + <header>radiation</header> + <header>rain</header> + <header>rh</header> + <header>wind</header> + <header>ETP</header> + <midnight>0</midnight> + </file> + </climate> + <notes> + <note> + <id>10.1016/j.eja.2019.125960</id> + <description>Future development of apricot blossom blight under climate change in Southern France</description> + </note> + <note> + <id>978-2-7148-0083-1</id> + <description>La ventilation des bâtiments d'élevage de ruminants, Institut du Végétal</description> + </note> + </notes> + <phenology> + <file path="../model/data/phenology/pheno_sample.csv"> + <separator>;</separator> + <header>year</header> + <header>s0</header> + <header>s1</header> + <header>s2</header> + <header>s3</header> + <header>s4</header> + </file> + </phenology> + <name>evaluation-test</name> + <type>WITH_AGGREGATION</type> + <compositeIndicator> + <name>root-test</name> + <name xml:lang="en">evaluation-test</name> + <id>root-evaluation</id> + <timescale>DAILY</timescale> + <tag>practices</tag> + <indicator xsi:type="compositeIndicator" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <name>s1</name> + <id>s0s1</id> + <category>pheno</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <tag>pheno-s0-s1</tag> + <indicator xsi:type="compositeIndicator"> + <name>s0</name> + <id>pheno_s0</id> + <category>pheno</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <normalizationFunction xsi:type="exponential" expA="0.0" expB="0.0"/> + <tag>pheno-s0-s1</tag> + </indicator> + <indicator xsi:type="compositeIndicator"> + <name xml:lang="en">Crop growth</name> + <name xml:lang="fr">Développement cultural</name> + <id>growth</id> + <category>ecoprocesses</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <indicator xsi:type="compositeIndicator"> + <name xml:lang="fr">Durée de la phase</name> + <name xml:lang="en">Phase length</name> + <id>phalength</id> + <category>climatic</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <indicator xsi:type="phaseLength"> + <description xml:lang="fr">Durée de la phase.</description> + <description xml:lang="en">Phase length.</description> + <name xml:lang="fr">Durée de la phase</name> + <name xml:lang="en">Phase length</name> + <id>phalen</id> + <category>indicator</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <normalizationFunction xsi:type="sigmoid" sigmoidA="30.0" sigmoidB="4.0"/> + <unit>day</unit> + </indicator> + </indicator> + </indicator> + </indicator> + </compositeIndicator> +</evaluationSettings> -- GitLab From 353025816a393c4ae15ec1b6f8e6a4dea60cbaea Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 14:40:47 +0200 Subject: [PATCH 03/15] =?UTF-8?q?D=C3=A9couper=20le=20test=20en=20plusieur?= =?UTF-8?q?s=20m=C3=A9thodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/EvaluationEachDateTest.java | 105 ++++++++++++------ .../indicators/xml/evaluation-phalen.gri | 16 +-- 2 files changed, 78 insertions(+), 43 deletions(-) diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java index 5b8c6daa..61ded8b0 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -29,44 +29,74 @@ import org.junit.Test; */ public class EvaluationEachDateTest extends DataTestHelper { + /** + * The only year in climatic data. + */ + private static final Integer YEAR = 2015; + /** * Evaluation from good XML file. */ - private Evaluation evaluation; + private static Evaluation evaluation; /** - * Set up Evaluation to test. + * Computation results. */ - @Before - public void beforeTest() { - final File xmlFile = getFile("xml/evaluation-phalen.gri"); - evaluation = getEvaluation(xmlFile); - } + private static Map<Date, Map<Integer, EvaluationResult>> results; + + /** + * DOY of Stage 0. + */ + private static final int S0 = 120; + + /** + * DOY of Stage 0 plus 1 day. + */ + private static final int S0_PLUS_1 = S0 + 1; + + /** + * DOY of Stage 1. + */ + private static final int S1 = 140; /** + * Set up Evaluation to test. + * * Check if evaluation computes. */ - @Test - public void computeEachDate() { + @BeforeClass + public static void beforeTest() { + final File xmlFile = getFile("xml/evaluation-phalen.gri"); + evaluation = getEvaluation(xmlFile); assertTrue("Evaluation must not be null!", evaluation != null); - final Map<Date, Map<Integer, EvaluationResult>> results; try { evaluation.initializeResources(); results = evaluation.computeEachDate(); } catch (final IndicatorsException e) { final String error = e.getClass() + " : " + e.getLocalizedMessage(); fail(error); - return; } + } + + /** + * Ensure only 2015 has data. + */ + @Test + public void years() { assertNotNull(results); assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); assertNotNull(years); assertEquals(1, years.size()); - final Integer expectedYear = 2015; - assertEquals(expectedYear, years.get(0)); + assertEquals(YEAR, years.get(0)); + } + /** + * Ensure only 1 indicator is computed. + */ + @Test + public void indicators() { // s0s1 : phalen final List<Indicator> indicators = new ArrayList<>(); final List<Indicator> allIndicators = new ArrayList<>(); @@ -86,26 +116,34 @@ public class EvaluationEachDateTest extends DataTestHelper { } final int nbOfIndicators = indicators.size(); assertEquals(1, nbOfIndicators); + } + /** + * Ensure no data is returned out of s0s1. + */ + @Test + public void noDataOutOfPhase() { // no data on 1st january 2015 as no phase final Optional<Date> firstDateOptional = results.keySet().stream().findFirst(); assertTrue(firstDateOptional.isPresent()); final Date firstDate = firstDateOptional.get(); - final Date expectedFirstDate = DateUtils.getDate(expectedYear, 1); + final Date expectedFirstDate = DateUtils.getDate(YEAR, 1); assertEquals(expectedFirstDate, firstDate); - assertFalse(results.get(firstDate).containsKey(expectedYear)); - - // s0s1 : 120 − 140 - final int s0 = 120; - final int s0Plus1 = s0 + 1; - final int s1 = 140; - List.of(s0Plus1, s1).forEach(doy -> { - final Date date = DateUtils.getDate(expectedYear, doy); - final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + assertFalse(results.get(firstDate).containsKey(YEAR)); + } + + /** + * Ensure data are return each date of s0s1. + */ + @Test + public void dataInPhase() { + for (int doy = S0_PLUS_1; doy < S1; doy++) { + final Date date = DateUtils.getDate(YEAR, doy); + final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); assertFalse(phaseResults.isEmpty()); assertEquals(1, phaseResults.size()); final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); - assertNotNull("On " + date + "/" + doy +", process results must not be null!", processResults); + assertNotNull("On " + date + "/" + doy + ", process results must not be null!", processResults); assertEquals(1, processResults.size()); assertEquals("growth", processResults.get(0).getIndicatorId()); @@ -116,18 +154,23 @@ public class EvaluationEachDateTest extends DataTestHelper { assertEquals("phalen", indicatorResults.get(0).getIndicatorId()); Double value = indicatorResults.get(0).getRawValue(); assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); - }); + } + } - // Check values - for (int doy = s0Plus1; doy < s1; doy++) { - final Date date = DateUtils.getDate(expectedYear, doy); - final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + /** + * Check computed values for each date. + */ + @Test + public void checkData() { + for (int doy = S0_PLUS_1; doy < S1; doy++) { + final Date date = DateUtils.getDate(YEAR, doy); + final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); final List<IndicatorResult> indicatorResults = practicesResults.get(0).getIndicatorResults(); final Double value = indicatorResults.get(0).getRawValue(); assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); - final Double expected = Double.valueOf(doy - s0); + final Double expected = Double.valueOf(doy - S0); assertEquals("On " + date + "/" + doy + ", phalen must equal " + expected, expected, value); } } diff --git a/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri index 5d756803..b11858c3 100644 --- a/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri +++ b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri @@ -2,7 +2,7 @@ <!DOCTYPE evaluationSettings PUBLIC "-//INRAE AgroClim.//DTD Evaluation 1.1//EN" "https://agroclim.inrae.fr/getari/dtd/1.1/evaluation.dtd"> -<evaluationSettings timescale="DAILY" timestamp="2024-08-19T13:43:21.127972376" version="1.2.2+20230809135003"> +<evaluationSettings timescale="DAILY" timestamp="2024-08-19T14:26:01.505524546" version="2.0.1+20240405062628"> <climate> <file path="../model/data/climate/climate-2015.csv"> <separator> </separator> @@ -22,16 +22,7 @@ <midnight>0</midnight> </file> </climate> - <notes> - <note> - <id>10.1016/j.eja.2019.125960</id> - <description>Future development of apricot blossom blight under climate change in Southern France</description> - </note> - <note> - <id>978-2-7148-0083-1</id> - <description>La ventilation des bâtiments d'élevage de ruminants, Institut du Végétal</description> - </note> - </notes> + <notes/> <phenology> <file path="../model/data/phenology/pheno_sample.csv"> <separator>;</separator> @@ -43,7 +34,7 @@ <header>s4</header> </file> </phenology> - <name>evaluation-test</name> + <name>evaluation-phalen</name> <type>WITH_AGGREGATION</type> <compositeIndicator> <name>root-test</name> @@ -51,6 +42,7 @@ <id>root-evaluation</id> <timescale>DAILY</timescale> <tag>practices</tag> + <aggregationFunction xsi:type="jexlFunction" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> <indicator xsi:type="compositeIndicator" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <name>s1</name> <id>s0s1</id> -- GitLab From 53b4ad231a3c08cfacbc2e33b4b52ee3b947deeb Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 15:00:23 +0200 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20:boom:=20Ajouter=20une=20m=C3=A9t?= =?UTF-8?q?hode=20pour=20calculer=20toutes=20les=20valeurs=20correspondant?= =?UTF-8?q?=20au=20jeu=20climatique=20fourni.=20refs=20agroclim/agrometinf?= =?UTF-8?q?o/AgroMetInfo=5F2.0#35?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refs agroclim/agrometinfo/AgroMetInfo_2.0#35 Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. --- pom.xml | 2 +- src/site/markdown/release-notes-fr.md | 28 +++++++++++++++++++++++++++ src/site/site.xml | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/site/markdown/release-notes-fr.md diff --git a/pom.xml b/pom.xml index d5dfc484..22fcaede 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ along with Indicators. If not, see <https://www.gnu.org/licenses/>. <name>Indicators</name> <description>Library of agro- and eco-climatic indicators.</description> <inceptionYear>2018</inceptionYear> - <version>2.0.3-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> <packaging>jar</packaging> <licenses> <license> diff --git a/src/site/markdown/release-notes-fr.md b/src/site/markdown/release-notes-fr.md new file mode 100644 index 00000000..e01a28e8 --- /dev/null +++ b/src/site/markdown/release-notes-fr.md @@ -0,0 +1,28 @@ +--- +title: Notes de version +description: Modifications de la bibliothèque d'indicateurs. +keywords: "version" +date: 2024-08-19 +--- + +# [v2.1.0](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.1.0) − + +- Ajouter la méthode `Evaluation.computeEachDate()`. +- Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. + +# [v2.0.2](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.2) − 11 juillet 2024 + +- Passer à Jakarta XML Binding. + +# [v2.0.1](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.1) − 5 avril 2024 + +- Ajouter le modèle phénologique Richardson. +- Renommer `CompositeIndicator.toAggregate(boolean)`. +- Corriger l'id et la référence d'indicateurs THI. + +# [v2.0.0](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.0) − 18 janvier 2024 + +- Gestion des données climatiques manquantes. +- Changement de gestion des exceptions et affichage de codes d'erreurs. +- Passage sous GitLab. +- Mise en ligne de la documentation. \ No newline at end of file diff --git a/src/site/site.xml b/src/site/site.xml index 0f490a99..89ea402e 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -30,6 +30,7 @@ <item name="Indicateurs journaliers" href="indicators-daily-fr.html" /> <item name="Codes d'erreurs" href="errors-fr.html" /> <item name="Modèles phénologiques" href="pheno-fr.html" /> + <item name="Notes de version" href="release-notes-fr.html" /> <item name="Documentation in English" href="en/index.html" /> </menu> <menu ref="modules" inherit="top" /> -- GitLab From 8591dc21874836138ac449de644884256920008e Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 15:49:06 +0200 Subject: [PATCH 05/15] Suppression de --- .../model/indicator/CompositeIndicator.java | 12 ------------ src/site/markdown/release-notes-fr.md | 4 +++- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java index e0b76d50..b639fc70 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java @@ -651,18 +651,6 @@ implements DataLoadingListener, Detailable, HasDataLoadingListener, Comparable<I getIndicators().forEach(i -> i.setParametersValues(values)); } - /** - * Detect if aggregation function is needed but missing. - * - * @param fire fire events while checking - * @return true if aggregation function is needed but missing - * @deprecated use {@link CompositeIndicator#isAggregationMissing(boolean)}. - */ - @Deprecated(since = "2.0.1", forRemoval = true) - public final boolean toAggregate(final boolean fire) { - return isAggregationMissing(fire); - } - @Override public final String toStringTree(final String indent) { final StringBuilder sb = new StringBuilder(); diff --git a/src/site/markdown/release-notes-fr.md b/src/site/markdown/release-notes-fr.md index e01a28e8..9a462c8a 100644 --- a/src/site/markdown/release-notes-fr.md +++ b/src/site/markdown/release-notes-fr.md @@ -9,6 +9,7 @@ date: 2024-08-19 - Ajouter la méthode `Evaluation.computeEachDate()`. - Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. +- Suppression de `CompositeIndicator#toAggregate(boolean)`. # [v2.0.2](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.2) − 11 juillet 2024 @@ -19,10 +20,11 @@ date: 2024-08-19 - Ajouter le modèle phénologique Richardson. - Renommer `CompositeIndicator.toAggregate(boolean)`. - Corriger l'id et la référence d'indicateurs THI. +- Dépréciation `CompositeIndicator#toAggregate(boolean)`. # [v2.0.0](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.0) − 18 janvier 2024 - Gestion des données climatiques manquantes. - Changement de gestion des exceptions et affichage de codes d'erreurs. - Passage sous GitLab. -- Mise en ligne de la documentation. \ No newline at end of file +- Mise en ligne de la documentation. -- GitLab From 61635723c18adb04b2c539322a414e5dcca9ce25 Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Tue, 20 Aug 2024 09:31:27 +0200 Subject: [PATCH 06/15] Utiliser LocalDate --- .../agroclim/indicators/model/Evaluation.java | 8 ++++---- .../indicators/model/data/HourlyData.java | 10 ++++++++++ .../indicators/model/EvaluationEachDateTest.java | 15 ++++++++------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java index 8dceebc1..e228def7 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java @@ -445,7 +445,7 @@ public final class Evaluation extends CompositeIndicator { * @throws IndicatorsException * from Indicator.compute() */ - public Map<Date, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException { + public Map<LocalDate, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException { this.ignoreEmptyClimaticData = true; final List<CompositeIndicator> phases = getPhases(); final ClimaticResource climaticResource = resourceManager.getClimaticResource(); @@ -453,16 +453,16 @@ public final class Evaluation extends CompositeIndicator { final List<ClimaticDailyData> dailyData = climaticResource.getData(); // first, create Maps - final Map<Date, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); - dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); + final Map<LocalDate, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); + dailyData.stream().map(DailyData::getLocalDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); // then compute final ClimaticResource resource = new ClimaticResource(); resource.setMissingVariables(climaticResource.getMissingVariables()); for (int i = 0; i < dailyData.size(); i++) { - final Date date = dailyData.get(i).getDate(); final List<ClimaticDailyData> data = dailyData.subList(0, i); resource.setData(data); final Map<Integer, EvaluationResult> results = compute(resource, phases); + final LocalDate date = dailyData.get(i).getLocalDate(); allResults.put(date, results); } return allResults; diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java b/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java index 5c6f5c28..97ecc147 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java @@ -21,6 +21,7 @@ import java.util.Calendar; import java.util.Date; import fr.inrae.agroclim.indicators.util.DateUtils; +import java.time.LocalDate; import lombok.Getter; /** @@ -174,6 +175,15 @@ public abstract class HourlyData implements Cloneable, Data, Serializable { return DateUtils.getDoy(getDate()); } + /** + * Create date object with {@code day}, {@code month}, {@code year} and {@code hour} attribute.<br> + * If at least one of these attributes is null, then the returned date is null. + * @return date object + */ + public LocalDate getLocalDate() { + return DateUtils.asLocalDate(getDate()); + } + /** * Get value for variable as it was set. * diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java index 61ded8b0..95dfba5c 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java @@ -9,8 +9,9 @@ import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; import fr.inrae.agroclim.indicators.util.DateUtils; import java.io.File; +import java.time.LocalDate; +import java.time.Month; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; @@ -42,7 +43,7 @@ public class EvaluationEachDateTest extends DataTestHelper { /** * Computation results. */ - private static Map<Date, Map<Integer, EvaluationResult>> results; + private static Map<LocalDate, Map<Integer, EvaluationResult>> results; /** * DOY of Stage 0. @@ -124,10 +125,10 @@ public class EvaluationEachDateTest extends DataTestHelper { @Test public void noDataOutOfPhase() { // no data on 1st january 2015 as no phase - final Optional<Date> firstDateOptional = results.keySet().stream().findFirst(); + final Optional<LocalDate> firstDateOptional = results.keySet().stream().findFirst(); assertTrue(firstDateOptional.isPresent()); - final Date firstDate = firstDateOptional.get(); - final Date expectedFirstDate = DateUtils.getDate(YEAR, 1); + final LocalDate firstDate = firstDateOptional.get(); + final LocalDate expectedFirstDate = LocalDate.of(YEAR, Month.JANUARY, 1); assertEquals(expectedFirstDate, firstDate); assertFalse(results.get(firstDate).containsKey(YEAR)); } @@ -138,7 +139,7 @@ public class EvaluationEachDateTest extends DataTestHelper { @Test public void dataInPhase() { for (int doy = S0_PLUS_1; doy < S1; doy++) { - final Date date = DateUtils.getDate(YEAR, doy); + final LocalDate date = DateUtils.asLocalDate(DateUtils.getDate(YEAR, doy)); final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); assertFalse(phaseResults.isEmpty()); assertEquals(1, phaseResults.size()); @@ -163,7 +164,7 @@ public class EvaluationEachDateTest extends DataTestHelper { @Test public void checkData() { for (int doy = S0_PLUS_1; doy < S1; doy++) { - final Date date = DateUtils.getDate(YEAR, doy); + final LocalDate date = DateUtils.asLocalDate(DateUtils.getDate(YEAR, doy)); final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); -- GitLab From db45e30371f2fa43347286e7763503e28fe39e0a Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Wed, 14 Aug 2024 17:02:23 +0200 Subject: [PATCH 07/15] =?UTF-8?q?Ajouter=20une=20m=C3=A9thode=20pour=20cal?= =?UTF-8?q?culer=20toutes=20les=20valeurs=20correspondant=20au=20jeu=20cli?= =?UTF-8?q?matique=20fourni.=20refs=20agroclim/agrometinfo/AgroMetInfo=5F2?= =?UTF-8?q?.0#35?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agroclim/indicators/model/Evaluation.java | 163 ++++++++++-------- .../model/indicator/CompositeIndicator.java | 10 +- .../indicators/model/indicator/Indicator.java | 14 +- .../model/EvaluationHourlyTest.java | 12 +- .../model/EvaluationRobertTest.java | 12 +- .../indicators/model/EvaluationTest.java | 51 ++++-- .../model/EvalutationCustomHeadersTest.java | 12 +- .../indicators/model/MMarjouTest.java | 14 +- .../indicators/model/RaidayMeantTest.java | 45 +++-- 9 files changed, 188 insertions(+), 145 deletions(-) diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java index 8e78e9e4..31baba23 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java @@ -16,8 +16,6 @@ */ package fr.inrae.agroclim.indicators.model; -import java.io.IOException; -import java.io.ObjectInputStream; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; @@ -106,21 +104,6 @@ public final class Evaluation extends CompositeIndicator { }); } - /** - * Computed phases (or phases from file) used to compute indicators. - * - * TODO : supprimer et utiliser - * EvaluationResult::getPhaseResults::getAnnualPhase. - */ - @Getter - private transient List<AnnualPhase> computedPhases; - - /** - * Results of computation by year. - */ - @Getter - private transient Map<Integer, EvaluationResult> results = new LinkedHashMap<>(); - /** * All resources needed to run the evaluation. * @@ -129,6 +112,11 @@ public final class Evaluation extends CompositeIndicator { @Getter private final ResourceManager resourceManager; + /** + * Flag to ignore when climatic data is empty for a phase. + */ + private boolean ignoreEmptyClimaticData = false; + /** * Flag for state Saved. */ @@ -198,12 +186,7 @@ public final class Evaluation extends CompositeIndicator { throw new RuntimeException("This should never occur!", ex); } } - if (evaluation.computedPhases != null) { - computedPhases = new ArrayList<>(); - computedPhases.addAll(evaluation.computedPhases); - } isTranscient = evaluation.isTranscient; - results = evaluation.results; state = evaluation.state; } @@ -285,11 +268,26 @@ public final class Evaluation extends CompositeIndicator { } /** - * Clear results of indicators. + * Check data before running compute* methods. + * + * @param phases phenological phases to check + * @param climaticResource climatic resource to check + * @throws IndicatorsException exception */ - private void clearResults() { - results.clear(); - computedPhases = new ArrayList<>(); + private void checkBeforeCompute(final List<CompositeIndicator> phases, final ClimaticResource climaticResource) + throws IndicatorsException { + if (phases == null) { + throw new RuntimeException("Phase list is null!"); + } + if (phases.isEmpty()) { + throw new RuntimeException("Phase list is empty!"); + } + if (climaticResource.isEmpty()) { + throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY); + } + if (resourceManager.getPhenologicalResource().isEmpty()) { + throw new IndicatorsException(ResourceErrorType.PHENO_EMPTY); + } } @Override @@ -300,32 +298,30 @@ public final class Evaluation extends CompositeIndicator { /** * Compute indicator results. * + * @return Results of computation by year. * @throws IndicatorsException * from Indicator.compute() */ - public void compute() throws IndicatorsException { + public Map<Integer, EvaluationResult> compute() throws IndicatorsException { LOGGER.trace("start computing evaluation \"" + getName() + "\""); + this.ignoreEmptyClimaticData = false; fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_START.event(this)); final List<CompositeIndicator> phases = getPhases(); - if (phases == null) { - throw new RuntimeException("Phase list is null!"); - } - if (phases.isEmpty()) { - throw new RuntimeException("Phase list is empty!"); - } - if (resourceManager.getClimaticResource().isEmpty()) { - throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY); - } - if (resourceManager.getPhenologicalResource().isEmpty()) { - throw new IndicatorsException(ResourceErrorType.PHENO_EMPTY); - } + final ClimaticResource climaticResource = resourceManager.getClimaticResource(); + checkBeforeCompute(phases, climaticResource); + + var results = compute(climaticResource, phases); + fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_SUCCESS.event(this)); + return results; + } - clearResults(); + private Map<Integer, EvaluationResult> compute(final ClimaticResource climaticResource, + final List<CompositeIndicator> phases) throws IndicatorsException { + final Map<Integer, EvaluationResult> results = new LinkedHashMap<>(); /* Pour chaque phase */ - List<AnnualStageData> stageDatas; - stageDatas = getResourceManager().getPhenologicalResource().getData(); + final List<AnnualStageData> stageDatas = getResourceManager().getPhenologicalResource().getData(); for (final CompositeIndicator phase : phases) { final String phaseId = phase.getId(); if (phaseId == null) { @@ -348,8 +344,7 @@ public final class Evaluation extends CompositeIndicator { dateYear -= 1; } - if (!resourceManager.getClimaticResource().getYears() - .contains(dateYear)) { + if (!climaticResource.getYears().contains(dateYear)) { continue; } @@ -368,7 +363,6 @@ public final class Evaluation extends CompositeIndicator { annualPhase.setEndStage(endStageName); annualPhase.setStartStage(startStageName); annualPhase.setUid(phaseId); - computedPhases.add(annualPhase); final PhaseResult phaseResult = new PhaseResult(); phaseResult.setAnnualPhase(annualPhase); results.get(year).getPhaseResults().add(phaseResult); @@ -399,15 +393,19 @@ public final class Evaluation extends CompositeIndicator { } /* Données climatiques pendant la phase et l'année donnée */ - final ClimaticResource climaticData = resourceManager - .getClimaticResource().getClimaticDataByPhaseAndYear(startDate, endDate); + final ClimaticResource climaticData = climaticResource + .getClimaticDataByPhaseAndYear(startDate, endDate); if (climaticData.isEmpty()) { - if (resourceManager.getClimaticResource().isEmpty()) { + if (climaticResource.isEmpty()) { throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY); } + if (ignoreEmptyClimaticData) { + continue; + } final int yearToSearch = dateYear; - final List<ClimaticDailyData> ddataList = resourceManager.getClimaticResource().getData() - .stream().filter(f -> f.getYear() == yearToSearch).collect(Collectors.toList()); + final List<ClimaticDailyData> ddataList = climaticResource.getData().stream() // + .filter(f -> f.getYear() == yearToSearch) // + .collect(Collectors.toList()); final ClimaticDailyData startData = ddataList.get(0); final ClimaticDailyData endData = ddataList.get(ddataList.size() - 1); @@ -433,9 +431,43 @@ public final class Evaluation extends CompositeIndicator { } } - computeFaisability(); - fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_SUCCESS.event(this)); + computeFaisability(results); LOGGER.trace("end of computing evaluation \"{}\"", getName()); + return results; + } + + /** + * Compute indicator results for each step of provided climatic data. + * + * @return Results of computation by year, for each step in climatic data. + * @throws IndicatorsException + * from Indicator.compute() + */ + public Map<Date, Map<Integer, EvaluationResult>> computeEach() throws IndicatorsException { + LOGGER.info("Start"); + this.ignoreEmptyClimaticData = true; + final List<CompositeIndicator> phases = getPhases(); + final ClimaticResource climaticResource = resourceManager.getClimaticResource(); + checkBeforeCompute(phases, climaticResource); + + final List<ClimaticDailyData> dailyData = climaticResource.getData(); + // first, create Maps + final Map<Date, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); + //dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); + // then compute + final ClimaticResource resource = new ClimaticResource(); + resource.setMissingVariables(climaticResource.getMissingVariables()); + for (int i = 0; i < dailyData.size(); i++) { + final Date date = dailyData.get(i).getDate(); + LOGGER.info("Compute {}", date); + final List<ClimaticDailyData> data = dailyData.subList(0, i); + resource.setData(data); + var results = compute(resource, phases); + LOGGER.info("Results : {}", results); + allResults.put(date, results); + } + LOGGER.info("End"); + return allResults; } /** @@ -448,9 +480,10 @@ public final class Evaluation extends CompositeIndicator { * Pour chaque année, Pour chaque phase, valeurs.onIndicatorAdd(valeur phase * p, année n); Fin pour aggregation(valeurs); Fin pour; * + * @param results Results of computation by year. * @throws IndicatorsException raised by AggregationFunction.aggregate() */ - private void computeFaisability() throws IndicatorsException { + private void computeFaisability(final Map<Integer, EvaluationResult> results) throws IndicatorsException { LOGGER.traceEntry(); if (getType() == EvaluationType.WITHOUT_AGGREGATION) { return; @@ -459,18 +492,14 @@ public final class Evaluation extends CompositeIndicator { // if only 1 phase, no need to aggregate // evaluation value = value of the phase results.values().forEach(result -> - result.setNormalizedValue( - result.getPhaseResults().get(0).getNormalizedValue() - ) - ); + result.setNormalizedValue(result.getPhaseResults().get(0).getNormalizedValue())); return; } else if (getAggregationFunction().getExpression() == null) { throw new IllegalStateException("An evaluation with more than 1 " + "phase must have a defined expression for aggregation!"); } - for (final Map.Entry<Integer, EvaluationResult> entry - : results.entrySet()) { + for (final Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) { final EvaluationResult evaluationResult = entry.getValue(); final int year = entry.getKey(); if (evaluationResult == null) { @@ -872,22 +901,6 @@ public final class Evaluation extends CompositeIndicator { return result; } - /** - * Context: Deserialization does not initialize results. - * - * A final field must be initialized either by direct assignment of an initial value or in the constructor. During - * deserialization, neither of these are invoked, so initial values for transients must be set in the - * 'readObject()' private method that's invoked during deserialization. And for that to work, the transients must - * be non-final. - * - * @param ois input stream from deserialization - */ - private void readObject(final ObjectInputStream ois) throws ClassNotFoundException, IOException { - // perform the default de-serialization first - ois.defaultReadObject(); - results = new HashMap<>(); - } - /** * Set parameters (id and attributes) for the indicator and its criteria * from knowledge defined in settings. diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java index c0af4e5e..e0b76d50 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java @@ -252,14 +252,14 @@ implements DataLoadingListener, Detailable, HasDataLoadingListener, Comparable<I } @Override - public final Double compute(final Resource<? extends DailyData> climRessource) throws IndicatorsException { - if (climRessource.getYears().isEmpty()) { + public final Double compute(final Resource<? extends DailyData> climResource) throws IndicatorsException { + if (climResource.getYears().isEmpty()) { throw new RuntimeException( String.format( "No years in ClimaticResource (%d dailyData)!", - climRessource.getData().size())); + climResource.getData().size())); } - final HashMap<String, Double> results = new HashMap<>(); + final Map<String, Double> results = new HashMap<>(); double valueAfterAggregation = 0; Double valueAfterNormalization; @@ -274,7 +274,7 @@ implements DataLoadingListener, Detailable, HasDataLoadingListener, Comparable<I } try { - final double value = indicator.compute(climRessource); + final Double value = indicator.compute(climResource); results.put(indicator.getId(), value); } catch (final IndicatorsException e) { throw new IndicatorsException(ComputationErrorType.COMPOSITE_COMPUTATION, e, indicator.getId()); diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java index 22969b6c..e383f79e 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/Indicator.java @@ -186,6 +186,7 @@ Serializable, UseVariables { * Normalized value. */ @XmlTransient + @Getter @Setter private Double value; @@ -225,8 +226,8 @@ Serializable, UseVariables { } } listeners.add(IndicatorListener.class, listener); - if (this instanceof CompositeIndicator) { - ((CompositeIndicator) this).getIndicators() + if (this instanceof CompositeIndicator compositeIndicator) { + compositeIndicator.getIndicators() .forEach(i -> i.addIndicatorListener(listener)); } } @@ -364,15 +365,6 @@ Serializable, UseVariables { */ public abstract EvaluationType getType(); - /** - * The normalized value. - * - * @return Normalized value - */ - public final Double getValue() { - return value; - } - /** * @param indicatorCategory indicator category */ diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java index f90f64f0..0de0bdb9 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationHourlyTest.java @@ -2,8 +2,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.util.List; @@ -52,16 +52,16 @@ public class EvaluationHourlyTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); assertNotNull(results); assertEquals(1, results.keySet().size()); assertTrue(results.containsKey(2015)); diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java index 8e99a6bc..38d60322 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationRobertTest.java @@ -20,8 +20,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.util.Arrays; @@ -80,16 +80,16 @@ public class EvaluationRobertTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); assertNotNull(results); assertEquals(2, results.keySet().size()); assertTrue(results.containsKey(1991)); diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java index a3f975d4..85bf316e 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.io.FileOutputStream; @@ -49,6 +50,7 @@ import fr.inrae.agroclim.indicators.model.indicator.Indicator; import fr.inrae.agroclim.indicators.model.result.EvaluationResult; import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; +import java.util.Date; import lombok.NonNull; import lombok.extern.log4j.Log4j2; @@ -128,6 +130,32 @@ public final class EvaluationTest extends DataTestHelper { assertNull(error, error); } + /** + * Check if evaluation computes. + */ + @Test + public void computeEach() { + assertTrue("Evaluation must not be null!", evaluation != null); + final Map<Date, Map<Integer, EvaluationResult>> results; + try { + evaluation.initializeResources(); + results = evaluation.computeEach(); + } catch (final IndicatorsException e) { + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; + } + assertNotNull(results); + + assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); + final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); + assertNotNull(years); + System.out.println(years); + assertEquals(1, years.size()); + final Integer expectedYear = 2015; + assertEquals(expectedYear, years.get(0)); + } + @Test public void computableWithNoData() { final File xmlFile = getEvaluationWithNoDataTestFile(); @@ -184,8 +212,11 @@ public final class EvaluationTest extends DataTestHelper { String error = null; try { evaluation.initializeResources(); - evaluation.compute(); - final List<AnnualPhase> phases = evaluation.getComputedPhases(); + final Map<Integer, EvaluationResult> results = evaluation.compute(); + final List<AnnualPhase> phases = results.values().stream() // + .flatMap(r -> r.getPhaseResults().stream()) // + .map(r -> r.getAnnualPhase()) // + .toList(); final int nbOfPhases = evaluation.getIndicators().size(); final int nbOfYears = evaluation.getClimaticResource().getYears().size(); assertEquals(nbOfPhases * nbOfYears, phases.size()); @@ -238,8 +269,7 @@ public final class EvaluationTest extends DataTestHelper { final String fmt = "%s | %4d | %s-%s | %f | %f"; try { evaluation.initializeResources(); - evaluation.compute(); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); + final Map<Integer, EvaluationResult> results = evaluation.compute(); assertNotNull("Results must not be null!", results); results.entrySet().forEach((entryER) -> { final Integer year = entryER.getKey(); @@ -420,15 +450,16 @@ public final class EvaluationTest extends DataTestHelper { public void setParameters() { assertTrue("Evaluation must not be null!", evaluation != null); String error = null; + Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); final Double initValue = getResultValue("s0s1", "heat", 2015, results); assertNotNull("value of climatic effect \"heat\" must not be null", initValue); @@ -438,11 +469,11 @@ public final class EvaluationTest extends DataTestHelper { parameters.put("Theat", 25.); evaluation.setParametersValues(parameters); try { - evaluation.compute(); + results = evaluation.compute(); } catch (IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); final Double newValue = getResultValue("s0s1", "heat", 2015, results); assertNotEquals("init = " + initValue + ", new = " + newValue, initValue, newValue); diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java index d226c65e..d02a249d 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvalutationCustomHeadersTest.java @@ -20,8 +20,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.util.Map; @@ -63,15 +63,15 @@ public class EvalutationCustomHeadersTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); - final Map<Integer, EvaluationResult> results = evaluation.getResults(); assertNotNull("Results must not be null!", results); assertEquals("One year en climate file", 1, results.keySet().size()); results.values().stream().flatMap(v -> v.getPhaseResults().stream()).forEach(phaseResult -> { diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java index 82613882..a102b23a 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/MMarjouTest.java @@ -17,7 +17,6 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.File; @@ -35,6 +34,7 @@ import fr.inrae.agroclim.indicators.model.result.EvaluationResult; import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; import lombok.extern.log4j.Log4j2; +import static org.junit.Assert.fail; /** * Test that Marine Marjou's Getari file computes. @@ -76,16 +76,17 @@ public class MMarjouTest extends DataTestHelper { @Test public void raidaysInfNbDaysInPhase() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); long start = System.currentTimeMillis(); - evaluation.compute(); + results = evaluation.compute(); LOGGER.info("Evaluation.compute() last {} seconds", (System.currentTimeMillis() - start) / 1000.); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); // extract stages to compute number of days Map<Integer, Map<String, Integer>> stageDates = new HashMap<>(); List<AnnualStageData> aSDatas; @@ -100,9 +101,8 @@ public class MMarjouTest extends DataTestHelper { }); }); // check excraidays, hraidays and raidays - assertFalse(evaluation.getResults().isEmpty()); + assertFalse(results.isEmpty()); String fmt = "%d | %s %s : %d | %s=%.4f"; - Map<Integer, EvaluationResult> results = evaluation.getResults(); for (Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) { Integer year = entry.getKey(); if (!stageDates.containsKey(year)) { diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java index c9c8ba33..c31942d7 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/RaidayMeantTest.java @@ -19,8 +19,8 @@ package fr.inrae.agroclim.indicators.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; @@ -208,8 +208,8 @@ public class RaidayMeantTest extends DataTestHelper { @Test public void computeUsingPhenologyCalculator() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; - List<AnnualStageData> annualStageDatas = null; + final List<AnnualStageData> annualStageDatas; + final Map<Integer, EvaluationResult> results; try { final EvaluationSettings settings = evaluation.getSettings(); // wheat Soissons @@ -221,25 +221,29 @@ public class RaidayMeantTest extends DataTestHelper { settings.getPhenologyLoader().setCalculator(calc); settings.getPhenologyLoader().setFile(null); evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); annualStageDatas = evaluation.getResourceManager().getPhenologicalResource().getData(); } catch (final IndicatorsException | IOException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); // first year in phenology file final int firstYear = 1959; assertNotNull(annualStageDatas); assertFalse(annualStageDatas.isEmpty()); assertTrue(firstYear == annualStageDatas.get(0).getYear()); - final List<AnnualPhase> computedPhases = evaluation.getComputedPhases(); + final List<AnnualPhase> computedPhases = results.values().stream() // + .flatMap(r -> r.getPhaseResults().stream()) // + .map(r -> r.getAnnualPhase()) // + .toList(); assertNotNull(computedPhases); assertFalse(computedPhases.isEmpty()); assertTrue(firstYear == computedPhases.get(0).getHarvestYear()); compareStagesAndPhases(annualStageDatas, computedPhases); - assertFalse(evaluation.getResults().isEmpty()); + assertFalse(results.isEmpty()); } /** @@ -248,14 +252,15 @@ public class RaidayMeantTest extends DataTestHelper { @Test public void compute() { assertTrue("Evaluation must not be null!", evaluation != null); - String error = null; + final Map<Integer, EvaluationResult> results; try { evaluation.initializeResources(); - evaluation.compute(); + results = evaluation.compute(); } catch (final IndicatorsException e) { - error = e.getClass() + " : " + e.getLocalizedMessage(); + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; } - assertNull(error, error); // extract stages to compute number of days final Map<Integer, Map<String, Integer>> stageDates = new HashMap<>(); List<AnnualStageData> aSDatas; @@ -271,9 +276,9 @@ public class RaidayMeantTest extends DataTestHelper { }); // check excraidays, hraidays and raidays - assertFalse(evaluation.getResults().isEmpty()); + assertFalse(results.isEmpty()); final String fmt = "%d | %s %s : %d | %s=%.4f"; - for (final Map.Entry<Integer, EvaluationResult> entry : evaluation.getResults().entrySet()) { + for (final Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) { final Integer year = entry.getKey(); if (!stageDates.containsKey(year)) { LOGGER.trace("No stage date for year {}", year); @@ -317,14 +322,16 @@ public class RaidayMeantTest extends DataTestHelper { } assertNotNull(aSDatas); - final List<AnnualPhase> computedPhases = evaluation.getComputedPhases(); + final List<AnnualPhase> computedPhases = results.values().stream() // + .flatMap(r -> r.getPhaseResults().stream()) // + .map(r -> r.getAnnualPhase()) // + .toList(); compareStagesAndPhases(aSDatas, computedPhases); - Map<String, Map<Integer, Map<String, Double>>> computation; - computation = new HashMap<>(); + final Map<String, Map<Integer, Map<String, Double>>> computation = new HashMap<>(); - evaluation.getResults().forEach((year, results) -> { - results.getPhaseResults().forEach((phase) -> { + results.forEach((year, r) -> { + r.getPhaseResults().forEach((phase) -> { final String phaseId = phase.getPhaseId(); if (!computation.containsKey(phaseId)) { computation.put(phaseId, new HashMap<>()); -- GitLab From b8b17e4028f3b1cd056d134a32e055453bb8c44e Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 14:23:29 +0200 Subject: [PATCH 08/15] Ajouter des tests --- .../agroclim/indicators/model/Evaluation.java | 14 +- .../model/result/EvaluationResult.java | 2 + .../model/EvaluationEachDateTest.java | 134 ++++++++++++++++++ .../indicators/model/EvaluationTest.java | 27 ---- .../indicators/xml/evaluation-phalen.gri | 100 +++++++++++++ 5 files changed, 242 insertions(+), 35 deletions(-) create mode 100644 src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java create mode 100644 src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java index 31baba23..8dceebc1 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import fr.inrae.agroclim.indicators.exception.IndicatorsException; import fr.inrae.agroclim.indicators.exception.type.ResourceErrorType; +import fr.inrae.agroclim.indicators.model.data.DailyData; import fr.inrae.agroclim.indicators.model.data.ResourceManager; import fr.inrae.agroclim.indicators.model.data.Variable; import fr.inrae.agroclim.indicators.model.data.Variable.Type; @@ -439,12 +440,12 @@ public final class Evaluation extends CompositeIndicator { /** * Compute indicator results for each step of provided climatic data. * - * @return Results of computation by year, for each step in climatic data. + * @return Results of computation by year, for each step in climatic data: + * Climatic date ⮕ (year, {@link EvalutationResult}). * @throws IndicatorsException * from Indicator.compute() */ - public Map<Date, Map<Integer, EvaluationResult>> computeEach() throws IndicatorsException { - LOGGER.info("Start"); + public Map<Date, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException { this.ignoreEmptyClimaticData = true; final List<CompositeIndicator> phases = getPhases(); final ClimaticResource climaticResource = resourceManager.getClimaticResource(); @@ -453,20 +454,17 @@ public final class Evaluation extends CompositeIndicator { final List<ClimaticDailyData> dailyData = climaticResource.getData(); // first, create Maps final Map<Date, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); - //dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); + dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); // then compute final ClimaticResource resource = new ClimaticResource(); resource.setMissingVariables(climaticResource.getMissingVariables()); for (int i = 0; i < dailyData.size(); i++) { final Date date = dailyData.get(i).getDate(); - LOGGER.info("Compute {}", date); final List<ClimaticDailyData> data = dailyData.subList(0, i); resource.setData(data); - var results = compute(resource, phases); - LOGGER.info("Results : {}", results); + final Map<Integer, EvaluationResult> results = compute(resource, phases); allResults.put(date, results); } - LOGGER.info("End"); return allResults; } diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java b/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java index 70782bf1..d0fba97c 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/result/EvaluationResult.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import lombok.Getter; +import lombok.ToString; /** * Normalized value of an evaluation for a year and values of composed phases. @@ -30,6 +31,7 @@ import lombok.Getter; * @author $Author$ * @version $Revision$ */ +@ToString public class EvaluationResult extends Result { /** diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java new file mode 100644 index 00000000..5b8c6daa --- /dev/null +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java @@ -0,0 +1,134 @@ +package fr.inrae.agroclim.indicators.model; + +import fr.inrae.agroclim.indicators.exception.IndicatorsException; +import fr.inrae.agroclim.indicators.model.data.DataTestHelper; +import fr.inrae.agroclim.indicators.model.indicator.CompositeIndicator; +import fr.inrae.agroclim.indicators.model.indicator.Indicator; +import fr.inrae.agroclim.indicators.model.result.EvaluationResult; +import fr.inrae.agroclim.indicators.model.result.IndicatorResult; +import fr.inrae.agroclim.indicators.model.result.PhaseResult; +import fr.inrae.agroclim.indicators.util.DateUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; + +/** + * Test {@link Evaluation#computeEachDate()}. + * + * @author Olivier Maury + */ +public class EvaluationEachDateTest extends DataTestHelper { + + /** + * Evaluation from good XML file. + */ + private Evaluation evaluation; + + /** + * Set up Evaluation to test. + */ + @Before + public void beforeTest() { + final File xmlFile = getFile("xml/evaluation-phalen.gri"); + evaluation = getEvaluation(xmlFile); + } + + /** + * Check if evaluation computes. + */ + @Test + public void computeEachDate() { + assertTrue("Evaluation must not be null!", evaluation != null); + final Map<Date, Map<Integer, EvaluationResult>> results; + try { + evaluation.initializeResources(); + results = evaluation.computeEachDate(); + } catch (final IndicatorsException e) { + final String error = e.getClass() + " : " + e.getLocalizedMessage(); + fail(error); + return; + } + assertNotNull(results); + + assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); + final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); + assertNotNull(years); + assertEquals(1, years.size()); + final Integer expectedYear = 2015; + assertEquals(expectedYear, years.get(0)); + + // s0s1 : phalen + final List<Indicator> indicators = new ArrayList<>(); + final List<Indicator> allIndicators = new ArrayList<>(); + allIndicators.addAll(evaluation.getIndicators()); + Indicator indicator = allIndicators.remove(0); + while (indicator != null) { + if (indicator instanceof CompositeIndicator c) { + allIndicators.addAll(c.getIndicators()); + } else { + indicators.add(indicator); + } + if (!allIndicators.isEmpty()) { + indicator = allIndicators.remove(0); + } else { + indicator = null; + } + } + final int nbOfIndicators = indicators.size(); + assertEquals(1, nbOfIndicators); + + // no data on 1st january 2015 as no phase + final Optional<Date> firstDateOptional = results.keySet().stream().findFirst(); + assertTrue(firstDateOptional.isPresent()); + final Date firstDate = firstDateOptional.get(); + final Date expectedFirstDate = DateUtils.getDate(expectedYear, 1); + assertEquals(expectedFirstDate, firstDate); + assertFalse(results.get(firstDate).containsKey(expectedYear)); + + // s0s1 : 120 − 140 + final int s0 = 120; + final int s0Plus1 = s0 + 1; + final int s1 = 140; + List.of(s0Plus1, s1).forEach(doy -> { + final Date date = DateUtils.getDate(expectedYear, doy); + final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + assertFalse(phaseResults.isEmpty()); + assertEquals(1, phaseResults.size()); + final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); + assertNotNull("On " + date + "/" + doy +", process results must not be null!", processResults); + assertEquals(1, processResults.size()); + assertEquals("growth", processResults.get(0).getIndicatorId()); + + // practices > growth > phalength > phalen + final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); + assertEquals("phalength", practicesResults.get(0).getIndicatorId()); + List<IndicatorResult> indicatorResults = practicesResults.get(0).getIndicatorResults(); + assertEquals("phalen", indicatorResults.get(0).getIndicatorId()); + Double value = indicatorResults.get(0).getRawValue(); + assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); + }); + + // Check values + for (int doy = s0Plus1; doy < s1; doy++) { + final Date date = DateUtils.getDate(expectedYear, doy); + final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); + final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); + final List<IndicatorResult> indicatorResults = practicesResults.get(0).getIndicatorResults(); + final Double value = indicatorResults.get(0).getRawValue(); + assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); + final Double expected = Double.valueOf(doy - s0); + assertEquals("On " + date + "/" + doy + ", phalen must equal " + expected, expected, value); + } + } +} diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java index 85bf316e..0603a053 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationTest.java @@ -50,7 +50,6 @@ import fr.inrae.agroclim.indicators.model.indicator.Indicator; import fr.inrae.agroclim.indicators.model.result.EvaluationResult; import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; -import java.util.Date; import lombok.NonNull; import lombok.extern.log4j.Log4j2; @@ -130,32 +129,6 @@ public final class EvaluationTest extends DataTestHelper { assertNull(error, error); } - /** - * Check if evaluation computes. - */ - @Test - public void computeEach() { - assertTrue("Evaluation must not be null!", evaluation != null); - final Map<Date, Map<Integer, EvaluationResult>> results; - try { - evaluation.initializeResources(); - results = evaluation.computeEach(); - } catch (final IndicatorsException e) { - final String error = e.getClass() + " : " + e.getLocalizedMessage(); - fail(error); - return; - } - assertNotNull(results); - - assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); - final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); - assertNotNull(years); - System.out.println(years); - assertEquals(1, years.size()); - final Integer expectedYear = 2015; - assertEquals(expectedYear, years.get(0)); - } - @Test public void computableWithNoData() { final File xmlFile = getEvaluationWithNoDataTestFile(); diff --git a/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri new file mode 100644 index 00000000..5d756803 --- /dev/null +++ b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!DOCTYPE evaluationSettings PUBLIC + "-//INRAE AgroClim.//DTD Evaluation 1.1//EN" + "https://agroclim.inrae.fr/getari/dtd/1.1/evaluation.dtd"> +<evaluationSettings timescale="DAILY" timestamp="2024-08-19T13:43:21.127972376" version="1.2.2+20230809135003"> + <climate> + <file path="../model/data/climate/climate-2015.csv"> + <separator> </separator> + <header></header> + <header>year</header> + <header>month</header> + <header>day</header> + <header>tmin</header> + <header>tmax</header> + <header>tmean</header> + <header></header> + <header>radiation</header> + <header>rain</header> + <header>rh</header> + <header>wind</header> + <header>ETP</header> + <midnight>0</midnight> + </file> + </climate> + <notes> + <note> + <id>10.1016/j.eja.2019.125960</id> + <description>Future development of apricot blossom blight under climate change in Southern France</description> + </note> + <note> + <id>978-2-7148-0083-1</id> + <description>La ventilation des bâtiments d'élevage de ruminants, Institut du Végétal</description> + </note> + </notes> + <phenology> + <file path="../model/data/phenology/pheno_sample.csv"> + <separator>;</separator> + <header>year</header> + <header>s0</header> + <header>s1</header> + <header>s2</header> + <header>s3</header> + <header>s4</header> + </file> + </phenology> + <name>evaluation-test</name> + <type>WITH_AGGREGATION</type> + <compositeIndicator> + <name>root-test</name> + <name xml:lang="en">evaluation-test</name> + <id>root-evaluation</id> + <timescale>DAILY</timescale> + <tag>practices</tag> + <indicator xsi:type="compositeIndicator" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <name>s1</name> + <id>s0s1</id> + <category>pheno</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <tag>pheno-s0-s1</tag> + <indicator xsi:type="compositeIndicator"> + <name>s0</name> + <id>pheno_s0</id> + <category>pheno</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <normalizationFunction xsi:type="exponential" expA="0.0" expB="0.0"/> + <tag>pheno-s0-s1</tag> + </indicator> + <indicator xsi:type="compositeIndicator"> + <name xml:lang="en">Crop growth</name> + <name xml:lang="fr">Développement cultural</name> + <id>growth</id> + <category>ecoprocesses</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <indicator xsi:type="compositeIndicator"> + <name xml:lang="fr">Durée de la phase</name> + <name xml:lang="en">Phase length</name> + <id>phalength</id> + <category>climatic</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <indicator xsi:type="phaseLength"> + <description xml:lang="fr">Durée de la phase.</description> + <description xml:lang="en">Phase length.</description> + <name xml:lang="fr">Durée de la phase</name> + <name xml:lang="en">Phase length</name> + <id>phalen</id> + <category>indicator</category> + <color>#5F9EA0</color> + <timescale>DAILY</timescale> + <normalizationFunction xsi:type="sigmoid" sigmoidA="30.0" sigmoidB="4.0"/> + <unit>day</unit> + </indicator> + </indicator> + </indicator> + </indicator> + </compositeIndicator> +</evaluationSettings> -- GitLab From 74f51ae2848d02001a515af0873b29a5289a36e0 Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 14:40:47 +0200 Subject: [PATCH 09/15] =?UTF-8?q?D=C3=A9couper=20le=20test=20en=20plusieur?= =?UTF-8?q?s=20m=C3=A9thodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/EvaluationEachDateTest.java | 105 ++++++++++++------ .../indicators/xml/evaluation-phalen.gri | 16 +-- 2 files changed, 78 insertions(+), 43 deletions(-) diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java index 5b8c6daa..61ded8b0 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -29,44 +29,74 @@ import org.junit.Test; */ public class EvaluationEachDateTest extends DataTestHelper { + /** + * The only year in climatic data. + */ + private static final Integer YEAR = 2015; + /** * Evaluation from good XML file. */ - private Evaluation evaluation; + private static Evaluation evaluation; /** - * Set up Evaluation to test. + * Computation results. */ - @Before - public void beforeTest() { - final File xmlFile = getFile("xml/evaluation-phalen.gri"); - evaluation = getEvaluation(xmlFile); - } + private static Map<Date, Map<Integer, EvaluationResult>> results; + + /** + * DOY of Stage 0. + */ + private static final int S0 = 120; + + /** + * DOY of Stage 0 plus 1 day. + */ + private static final int S0_PLUS_1 = S0 + 1; + + /** + * DOY of Stage 1. + */ + private static final int S1 = 140; /** + * Set up Evaluation to test. + * * Check if evaluation computes. */ - @Test - public void computeEachDate() { + @BeforeClass + public static void beforeTest() { + final File xmlFile = getFile("xml/evaluation-phalen.gri"); + evaluation = getEvaluation(xmlFile); assertTrue("Evaluation must not be null!", evaluation != null); - final Map<Date, Map<Integer, EvaluationResult>> results; try { evaluation.initializeResources(); results = evaluation.computeEachDate(); } catch (final IndicatorsException e) { final String error = e.getClass() + " : " + e.getLocalizedMessage(); fail(error); - return; } + } + + /** + * Ensure only 2015 has data. + */ + @Test + public void years() { assertNotNull(results); assertEquals(evaluation.getClimaticResource().getData().size(), results.size()); final List<Integer> years = results.values().stream().flatMap(m -> m.keySet().stream()).distinct().toList(); assertNotNull(years); assertEquals(1, years.size()); - final Integer expectedYear = 2015; - assertEquals(expectedYear, years.get(0)); + assertEquals(YEAR, years.get(0)); + } + /** + * Ensure only 1 indicator is computed. + */ + @Test + public void indicators() { // s0s1 : phalen final List<Indicator> indicators = new ArrayList<>(); final List<Indicator> allIndicators = new ArrayList<>(); @@ -86,26 +116,34 @@ public class EvaluationEachDateTest extends DataTestHelper { } final int nbOfIndicators = indicators.size(); assertEquals(1, nbOfIndicators); + } + /** + * Ensure no data is returned out of s0s1. + */ + @Test + public void noDataOutOfPhase() { // no data on 1st january 2015 as no phase final Optional<Date> firstDateOptional = results.keySet().stream().findFirst(); assertTrue(firstDateOptional.isPresent()); final Date firstDate = firstDateOptional.get(); - final Date expectedFirstDate = DateUtils.getDate(expectedYear, 1); + final Date expectedFirstDate = DateUtils.getDate(YEAR, 1); assertEquals(expectedFirstDate, firstDate); - assertFalse(results.get(firstDate).containsKey(expectedYear)); - - // s0s1 : 120 − 140 - final int s0 = 120; - final int s0Plus1 = s0 + 1; - final int s1 = 140; - List.of(s0Plus1, s1).forEach(doy -> { - final Date date = DateUtils.getDate(expectedYear, doy); - final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + assertFalse(results.get(firstDate).containsKey(YEAR)); + } + + /** + * Ensure data are return each date of s0s1. + */ + @Test + public void dataInPhase() { + for (int doy = S0_PLUS_1; doy < S1; doy++) { + final Date date = DateUtils.getDate(YEAR, doy); + final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); assertFalse(phaseResults.isEmpty()); assertEquals(1, phaseResults.size()); final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); - assertNotNull("On " + date + "/" + doy +", process results must not be null!", processResults); + assertNotNull("On " + date + "/" + doy + ", process results must not be null!", processResults); assertEquals(1, processResults.size()); assertEquals("growth", processResults.get(0).getIndicatorId()); @@ -116,18 +154,23 @@ public class EvaluationEachDateTest extends DataTestHelper { assertEquals("phalen", indicatorResults.get(0).getIndicatorId()); Double value = indicatorResults.get(0).getRawValue(); assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); - }); + } + } - // Check values - for (int doy = s0Plus1; doy < s1; doy++) { - final Date date = DateUtils.getDate(expectedYear, doy); - final List<PhaseResult> phaseResults = results.get(date).get(expectedYear).getPhaseResults(); + /** + * Check computed values for each date. + */ + @Test + public void checkData() { + for (int doy = S0_PLUS_1; doy < S1; doy++) { + final Date date = DateUtils.getDate(YEAR, doy); + final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); final List<IndicatorResult> indicatorResults = practicesResults.get(0).getIndicatorResults(); final Double value = indicatorResults.get(0).getRawValue(); assertNotNull("On " + date + "/" + doy + ", phalen must not be null!", value); - final Double expected = Double.valueOf(doy - s0); + final Double expected = Double.valueOf(doy - S0); assertEquals("On " + date + "/" + doy + ", phalen must equal " + expected, expected, value); } } diff --git a/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri index 5d756803..b11858c3 100644 --- a/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri +++ b/src/test/resources/fr/inrae/agroclim/indicators/xml/evaluation-phalen.gri @@ -2,7 +2,7 @@ <!DOCTYPE evaluationSettings PUBLIC "-//INRAE AgroClim.//DTD Evaluation 1.1//EN" "https://agroclim.inrae.fr/getari/dtd/1.1/evaluation.dtd"> -<evaluationSettings timescale="DAILY" timestamp="2024-08-19T13:43:21.127972376" version="1.2.2+20230809135003"> +<evaluationSettings timescale="DAILY" timestamp="2024-08-19T14:26:01.505524546" version="2.0.1+20240405062628"> <climate> <file path="../model/data/climate/climate-2015.csv"> <separator> </separator> @@ -22,16 +22,7 @@ <midnight>0</midnight> </file> </climate> - <notes> - <note> - <id>10.1016/j.eja.2019.125960</id> - <description>Future development of apricot blossom blight under climate change in Southern France</description> - </note> - <note> - <id>978-2-7148-0083-1</id> - <description>La ventilation des bâtiments d'élevage de ruminants, Institut du Végétal</description> - </note> - </notes> + <notes/> <phenology> <file path="../model/data/phenology/pheno_sample.csv"> <separator>;</separator> @@ -43,7 +34,7 @@ <header>s4</header> </file> </phenology> - <name>evaluation-test</name> + <name>evaluation-phalen</name> <type>WITH_AGGREGATION</type> <compositeIndicator> <name>root-test</name> @@ -51,6 +42,7 @@ <id>root-evaluation</id> <timescale>DAILY</timescale> <tag>practices</tag> + <aggregationFunction xsi:type="jexlFunction" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> <indicator xsi:type="compositeIndicator" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <name>s1</name> <id>s0s1</id> -- GitLab From 181043c6c7df8e16aaa6f86fe9fdb41189bf1542 Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 15:00:23 +0200 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20:boom:=20Ajouter=20une=20m=C3=A9t?= =?UTF-8?q?hode=20pour=20calculer=20toutes=20les=20valeurs=20correspondant?= =?UTF-8?q?=20au=20jeu=20climatique=20fourni.=20refs=20agroclim/agrometinf?= =?UTF-8?q?o/AgroMetInfo=5F2.0#35?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refs agroclim/agrometinfo/AgroMetInfo_2.0#35 Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. --- pom.xml | 2 +- src/site/markdown/release-notes-fr.md | 28 +++++++++++++++++++++++++++ src/site/site.xml | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/site/markdown/release-notes-fr.md diff --git a/pom.xml b/pom.xml index 73b60187..8aaf73ca 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ along with Indicators. If not, see <https://www.gnu.org/licenses/>. <name>Indicators</name> <description>Library of agro- and eco-climatic indicators.</description> <inceptionYear>2018</inceptionYear> - <version>2.0.3-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> <packaging>jar</packaging> <licenses> <license> diff --git a/src/site/markdown/release-notes-fr.md b/src/site/markdown/release-notes-fr.md new file mode 100644 index 00000000..e01a28e8 --- /dev/null +++ b/src/site/markdown/release-notes-fr.md @@ -0,0 +1,28 @@ +--- +title: Notes de version +description: Modifications de la bibliothèque d'indicateurs. +keywords: "version" +date: 2024-08-19 +--- + +# [v2.1.0](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.1.0) − + +- Ajouter la méthode `Evaluation.computeEachDate()`. +- Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. + +# [v2.0.2](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.2) − 11 juillet 2024 + +- Passer à Jakarta XML Binding. + +# [v2.0.1](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.1) − 5 avril 2024 + +- Ajouter le modèle phénologique Richardson. +- Renommer `CompositeIndicator.toAggregate(boolean)`. +- Corriger l'id et la référence d'indicateurs THI. + +# [v2.0.0](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.0) − 18 janvier 2024 + +- Gestion des données climatiques manquantes. +- Changement de gestion des exceptions et affichage de codes d'erreurs. +- Passage sous GitLab. +- Mise en ligne de la documentation. \ No newline at end of file diff --git a/src/site/site.xml b/src/site/site.xml index 0f490a99..89ea402e 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -30,6 +30,7 @@ <item name="Indicateurs journaliers" href="indicators-daily-fr.html" /> <item name="Codes d'erreurs" href="errors-fr.html" /> <item name="Modèles phénologiques" href="pheno-fr.html" /> + <item name="Notes de version" href="release-notes-fr.html" /> <item name="Documentation in English" href="en/index.html" /> </menu> <menu ref="modules" inherit="top" /> -- GitLab From 7c1d888ea62aecd237983f3df595c18521ea204d Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 19 Aug 2024 15:49:06 +0200 Subject: [PATCH 11/15] Suppression de --- .../model/indicator/CompositeIndicator.java | 12 ------------ src/site/markdown/release-notes-fr.md | 4 +++- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java index e0b76d50..b639fc70 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/indicator/CompositeIndicator.java @@ -651,18 +651,6 @@ implements DataLoadingListener, Detailable, HasDataLoadingListener, Comparable<I getIndicators().forEach(i -> i.setParametersValues(values)); } - /** - * Detect if aggregation function is needed but missing. - * - * @param fire fire events while checking - * @return true if aggregation function is needed but missing - * @deprecated use {@link CompositeIndicator#isAggregationMissing(boolean)}. - */ - @Deprecated(since = "2.0.1", forRemoval = true) - public final boolean toAggregate(final boolean fire) { - return isAggregationMissing(fire); - } - @Override public final String toStringTree(final String indent) { final StringBuilder sb = new StringBuilder(); diff --git a/src/site/markdown/release-notes-fr.md b/src/site/markdown/release-notes-fr.md index e01a28e8..9a462c8a 100644 --- a/src/site/markdown/release-notes-fr.md +++ b/src/site/markdown/release-notes-fr.md @@ -9,6 +9,7 @@ date: 2024-08-19 - Ajouter la méthode `Evaluation.computeEachDate()`. - Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. +- Suppression de `CompositeIndicator#toAggregate(boolean)`. # [v2.0.2](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.2) − 11 juillet 2024 @@ -19,10 +20,11 @@ date: 2024-08-19 - Ajouter le modèle phénologique Richardson. - Renommer `CompositeIndicator.toAggregate(boolean)`. - Corriger l'id et la référence d'indicateurs THI. +- Dépréciation `CompositeIndicator#toAggregate(boolean)`. # [v2.0.0](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.0) − 18 janvier 2024 - Gestion des données climatiques manquantes. - Changement de gestion des exceptions et affichage de codes d'erreurs. - Passage sous GitLab. -- Mise en ligne de la documentation. \ No newline at end of file +- Mise en ligne de la documentation. -- GitLab From 7fdc301c9f3e13a72d098ed35234d586c0146d1f Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Tue, 20 Aug 2024 09:31:27 +0200 Subject: [PATCH 12/15] Utiliser LocalDate --- .../agroclim/indicators/model/Evaluation.java | 8 ++++---- .../indicators/model/data/HourlyData.java | 10 ++++++++++ .../indicators/model/EvaluationEachDateTest.java | 15 ++++++++------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java index 8dceebc1..e228def7 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/Evaluation.java @@ -445,7 +445,7 @@ public final class Evaluation extends CompositeIndicator { * @throws IndicatorsException * from Indicator.compute() */ - public Map<Date, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException { + public Map<LocalDate, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException { this.ignoreEmptyClimaticData = true; final List<CompositeIndicator> phases = getPhases(); final ClimaticResource climaticResource = resourceManager.getClimaticResource(); @@ -453,16 +453,16 @@ public final class Evaluation extends CompositeIndicator { final List<ClimaticDailyData> dailyData = climaticResource.getData(); // first, create Maps - final Map<Date, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); - dailyData.stream().map(DailyData::getDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); + final Map<LocalDate, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>(); + dailyData.stream().map(DailyData::getLocalDate).forEach(date -> allResults.put(date, new LinkedHashMap<>())); // then compute final ClimaticResource resource = new ClimaticResource(); resource.setMissingVariables(climaticResource.getMissingVariables()); for (int i = 0; i < dailyData.size(); i++) { - final Date date = dailyData.get(i).getDate(); final List<ClimaticDailyData> data = dailyData.subList(0, i); resource.setData(data); final Map<Integer, EvaluationResult> results = compute(resource, phases); + final LocalDate date = dailyData.get(i).getLocalDate(); allResults.put(date, results); } return allResults; diff --git a/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java b/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java index 5c6f5c28..97ecc147 100644 --- a/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java +++ b/src/main/java/fr/inrae/agroclim/indicators/model/data/HourlyData.java @@ -21,6 +21,7 @@ import java.util.Calendar; import java.util.Date; import fr.inrae.agroclim.indicators.util.DateUtils; +import java.time.LocalDate; import lombok.Getter; /** @@ -174,6 +175,15 @@ public abstract class HourlyData implements Cloneable, Data, Serializable { return DateUtils.getDoy(getDate()); } + /** + * Create date object with {@code day}, {@code month}, {@code year} and {@code hour} attribute.<br> + * If at least one of these attributes is null, then the returned date is null. + * @return date object + */ + public LocalDate getLocalDate() { + return DateUtils.asLocalDate(getDate()); + } + /** * Get value for variable as it was set. * diff --git a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java index 61ded8b0..95dfba5c 100644 --- a/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java +++ b/src/test/java/fr/inrae/agroclim/indicators/model/EvaluationEachDateTest.java @@ -9,8 +9,9 @@ import fr.inrae.agroclim.indicators.model.result.IndicatorResult; import fr.inrae.agroclim.indicators.model.result.PhaseResult; import fr.inrae.agroclim.indicators.util.DateUtils; import java.io.File; +import java.time.LocalDate; +import java.time.Month; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; @@ -42,7 +43,7 @@ public class EvaluationEachDateTest extends DataTestHelper { /** * Computation results. */ - private static Map<Date, Map<Integer, EvaluationResult>> results; + private static Map<LocalDate, Map<Integer, EvaluationResult>> results; /** * DOY of Stage 0. @@ -124,10 +125,10 @@ public class EvaluationEachDateTest extends DataTestHelper { @Test public void noDataOutOfPhase() { // no data on 1st january 2015 as no phase - final Optional<Date> firstDateOptional = results.keySet().stream().findFirst(); + final Optional<LocalDate> firstDateOptional = results.keySet().stream().findFirst(); assertTrue(firstDateOptional.isPresent()); - final Date firstDate = firstDateOptional.get(); - final Date expectedFirstDate = DateUtils.getDate(YEAR, 1); + final LocalDate firstDate = firstDateOptional.get(); + final LocalDate expectedFirstDate = LocalDate.of(YEAR, Month.JANUARY, 1); assertEquals(expectedFirstDate, firstDate); assertFalse(results.get(firstDate).containsKey(YEAR)); } @@ -138,7 +139,7 @@ public class EvaluationEachDateTest extends DataTestHelper { @Test public void dataInPhase() { for (int doy = S0_PLUS_1; doy < S1; doy++) { - final Date date = DateUtils.getDate(YEAR, doy); + final LocalDate date = DateUtils.asLocalDate(DateUtils.getDate(YEAR, doy)); final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); assertFalse(phaseResults.isEmpty()); assertEquals(1, phaseResults.size()); @@ -163,7 +164,7 @@ public class EvaluationEachDateTest extends DataTestHelper { @Test public void checkData() { for (int doy = S0_PLUS_1; doy < S1; doy++) { - final Date date = DateUtils.getDate(YEAR, doy); + final LocalDate date = DateUtils.asLocalDate(DateUtils.getDate(YEAR, doy)); final List<PhaseResult> phaseResults = results.get(date).get(YEAR).getPhaseResults(); final List<IndicatorResult> processResults = phaseResults.get(0).getIndicatorResults(); final List<IndicatorResult> practicesResults = processResults.get(0).getIndicatorResults(); -- GitLab From 8b5e9bfeebfce7959a7ae1104ee62415d77036e8 Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Wed, 21 Aug 2024 14:50:47 +0200 Subject: [PATCH 13/15] =?UTF-8?q?build(deps):=20:arrow=5Fup:=20passer=20?= =?UTF-8?q?=C3=A0=20maven-source-plugin-3.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8aaf73ca..c354dd73 100644 --- a/pom.xml +++ b/pom.xml @@ -382,11 +382,11 @@ along with Indicators. If not, see <https://www.gnu.org/licenses/>. </executions> </plugin> <!-- Attach source and javadoc artifacts --> - <!-- https://maven.apache.org/plugin-developers/cookbook/attach-source-javadoc-artifacts.html --> + <!-- https://maven.apache.org/plugins/maven-source-plugin/usage.html --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> - <version>3.3.0</version> + <version>3.3.1</version> <executions> <execution> <id>attach-sources</id> -- GitLab From 2d1244bf1414fe6f7cb5e752234aedca50ef7008 Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Wed, 21 Aug 2024 14:57:24 +0200 Subject: [PATCH 14/15] issue_template --- .gitlab/issue_templates/publier_nouvelle_version.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab/issue_templates/publier_nouvelle_version.md b/.gitlab/issue_templates/publier_nouvelle_version.md index 908e8149..9ba22f2a 100644 --- a/.gitlab/issue_templates/publier_nouvelle_version.md +++ b/.gitlab/issue_templates/publier_nouvelle_version.md @@ -3,9 +3,10 @@ Pour passer de la version *SNAPSHOT* à la version stable : - [ ] créer une demande de fusion et une branche à partir du ticket - [ ] changer la version dans `pom.xml` ```sh - mvn versions:set -DnewVersion=2.0.2 + mvn versions:set -DnewVersion=2.1.0 mvn versions:commit ``` +- [ ] mettre à jour `src/site/markdown/release-notes-fr.md` - [ ] mettre à jour les fichiers de métadonnées du projet - [ ] fusionner - [ ] déployer sur Archiva -- GitLab From 6b3332c9f1c6585c6a713bef0a915ea1b3c60bda Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Wed, 21 Aug 2024 15:06:05 +0200 Subject: [PATCH 15/15] release notes --- src/site/markdown/release-notes-fr.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/site/markdown/release-notes-fr.md b/src/site/markdown/release-notes-fr.md index 9a462c8a..d9c7ccf1 100644 --- a/src/site/markdown/release-notes-fr.md +++ b/src/site/markdown/release-notes-fr.md @@ -9,7 +9,9 @@ date: 2024-08-19 - Ajouter la méthode `Evaluation.computeEachDate()`. - Renvoyer les résultats de calcul par la méthode `Evaluation.compute()` et ne plus les stocker dans `Evaluation`. -- Suppression de `CompositeIndicator#toAggregate(boolean)`. +- Supprimer `Evaluation#getResults()`. +- Supprimer `Evaluation#getComputedPhases()`. +- Supprimer de `CompositeIndicator#toAggregate(boolean)`. # [v2.0.2](https://forgemia.inra.fr/agroclim/Indicators/indicators-java/-/releases/v2.0.2) − 11 juillet 2024 -- GitLab