[Git][ultreiaio/ird-observe][feature/issue-2946-2] Introduce WithSpeciesFaoCode and WithFormula from toolkit project.
Tony CHEMIT pushed to branch feature/issue-2946-2 at ultreiaio / ird-observe Commits: dada0ce7 by Tony Chemit at 2024-10-02T19:05:50+02:00 Introduce WithSpeciesFaoCode and WithFormula from toolkit project. - - - - - 4 changed files: - + core/api/dto/src/main/java/fr/ird/observe/dto/referential/FormulaHelper.java - + core/api/dto/src/main/java/fr/ird/observe/dto/referential/WithFormula.java - + core/api/dto/src/main/java/fr/ird/observe/dto/referential/WithSpeciesFaoCode.java - + core/api/dto/src/test/java/fr/ird/observe/dto/referential/FormulaHelperTest.java Changes: ===================================== core/api/dto/src/main/java/fr/ird/observe/dto/referential/FormulaHelper.java ===================================== @@ -0,0 +1,184 @@ +package fr.ird.observe.dto.referential; + +/*- + * #%L + * ObServe Core :: API :: Dto + * %% + * Copyright (C) 2008 - 2024 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.dto.ObserveUtil; +import io.ultreia.java4all.lang.Strings; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created on 05/11/16. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 6.0 + */ +public class FormulaHelper { + + public static final String VARIABLE_WEIGHT = "P"; + public static final String VARIABLE_LENGTH = "L"; + public static final String VARIABLE_INPUT = "I"; + public static final String VARIABLE_OUTPUT = "O"; + public static final String COEFFICIENT_B = "b"; + private static final Logger log = LogManager.getLogger(FormulaHelper.class); + private static final Pattern COEFFICIENTS_PATTERN = Pattern.compile("(.+)=(.+)"); + private static final String VARIABLE_X = "x"; + private static ScriptEngine scriptEngine; + + private static ScriptEngine getScriptEngine() { + if (scriptEngine == null) { + scriptEngine = ObserveUtil.getScriptEngine(); + } + return scriptEngine; + } + + public static Map<String, Double> getCoefficientValues(WithFormula formula) { + Map<String, Double> result = new TreeMap<>(); + String coefficients = formula.getCoefficients(); + if (coefficients != null) { + for (String coefficientDef : coefficients.split(":")) { + Matcher matcher = COEFFICIENTS_PATTERN.matcher(coefficientDef.trim()); + log.debug("constant to test = " + coefficientDef); + if (matcher.matches()) { + String key = matcher.group(1); + String val = matcher.group(2); + try { + Double d = Double.valueOf(val); + result.put(key, d); + log.debug("detects coefficient " + key + '=' + val); + } catch (NumberFormatException e) { + // pas pu recupere le count... + log.warn("could not parse double " + val + " for coefficient " + key, e); + } + } + } + } + return result; + } + + public static boolean validateRelation(WithFormula withFormula, String formula, String variable) { + boolean result = false; + if (Strings.isNotEmpty(formula)) { + Map<String, Double> coefficientValues = withFormula.getCoefficientValues(); + ScriptEngine engine = getScriptEngine(); + Bindings bindings = engine.createBindings(); + addBindings(coefficientValues, bindings, variable, 1); + try { + engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + Number o = (Number) engine.eval("parseFloat(" + formula + ")"); + log.debug("evaluation ok : " + formula + " (" + variable + "=1) = " + o); + result = true; + } catch (Exception e) { + log.error("evaluation ko : " + formula + ", reason : " + e.getMessage(), e); + } + } + return result; + } + + public static boolean validateObjectMaterialValidation(String relation, Object value) { + boolean result = false; + if (Strings.isNotEmpty(relation)) { + ScriptEngine engine = getScriptEngine(); + Bindings bindings = engine.createBindings(); + bindings.put(FormulaHelper.VARIABLE_X, value); + try { + engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + Boolean o = (Boolean) engine.eval(relation); + log.debug(String.format("evaluation ok : %s (%s=%s) = %s", relation, FormulaHelper.VARIABLE_X, value, o)); + result = o != null && o; + } catch (Exception e) { + log.error("evaluation ko : " + relation + ", reason : " + e.getMessage(), e); + } + } + return result; + } + + public static boolean validateObjectMaterialValidationSyntax(String relation, Object value) { + if (Strings.isNotEmpty(relation)) { + ScriptEngine engine = getScriptEngine(); + Bindings bindings = engine.createBindings(); + bindings.put(FormulaHelper.VARIABLE_X, value); + try { + engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + Boolean o = (Boolean) engine.eval(relation); + log.debug(String.format("evaluation ok : %s (%s=%s) = %s", relation, FormulaHelper.VARIABLE_X, value, o)); + return true; + } catch (Exception e) { + log.error(String.format("evaluation ko : %s, reason : %s", relation, e.getMessage()), e); + return false; + } + } + return false; + } + + public static Float computeValue(WithFormula withFormula, String formula, String coefficientName, String variableName, float data, Function<Float,Float> toRoundResult) { + if (coefficientName != null) { + Double b = withFormula.getCoefficientValue(coefficientName); + if (b == 0) { + // ce cas limite ne permet pas de calculer la taille a partir du weight + return null; + } + } + Float o = computeValue(withFormula, formula, variableName, data); + if (o != null) { + o = toRoundResult.apply(o); + } + return o; + } + + private static Float computeValue(WithFormula withFormula, String formula, String variable, float data) { + Map<String, Double> coefficientValues = withFormula.getCoefficientValues(); + ScriptEngine engine = getScriptEngine(); + Bindings bindings = engine.createBindings(); + addBindings(coefficientValues, bindings, variable, data); + engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + Number o = null; + try { + o = (Number) engine.eval("parseFloat(" + formula + ")"); + } catch (ScriptException e) { + log.error("Could not compute value from " + formula, e); + } + return o == null ? null : o.floatValue(); + } + + private static void addBindings(Map<String, Double> coefficientValues, Bindings bindings, String variable, float data) { + for (Map.Entry<String, Double> entry : coefficientValues.entrySet()) { + String key = entry.getKey(); + Double value = entry.getValue(); + bindings.put(key, value); + log.debug("add constant " + key + '=' + value); + } + bindings.put(variable, data); + } + +} ===================================== core/api/dto/src/main/java/fr/ird/observe/dto/referential/WithFormula.java ===================================== @@ -0,0 +1,114 @@ +package fr.ird.observe.dto.referential; + +/*- + * #%L + * ObServe Core :: API :: Dto + * %% + * Copyright (C) 2008 - 2024 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.dto.WithStartEndDate; + +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created on 22/12/16. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 6.0 + */ +public interface WithFormula extends WithStartEndDate, WithSpeciesFaoCode { + + String PROPERTY_OCEAN = "ocean"; + String PROPERTY_SPECIES = "species"; + String PROPERTY_SEX = "sex"; + String PROPERTY_START_DATE = "startDate"; + String PROPERTY_END_DATE = "endDate"; + String PROPERTY_COEFFICIENTS = "coefficients"; + String PROPERTY_SOURCE = "source"; + Comparator<WithFormula> FORMULA_SUPPORT_START_DATE_COMPARATOR = Comparator.comparing(WithStartEndDate::getStartDate, WithStartEndDate.START_DATE_COMPARATOR); + Comparator<WithFormula> FORMULA_SUPPORT_END_DATE_COMPARATOR = Comparator.comparing(WithStartEndDate::getEndDate, WithStartEndDate.END_DATE_COMPARATOR); + Comparator<WithFormula> FORMULA_SUPPORT_COMPARATOR = FORMULA_SUPPORT_START_DATE_COMPARATOR.thenComparing(FORMULA_SUPPORT_END_DATE_COMPARATOR); + + static <D extends WithFormula> void sort(List<D> list) { + list.sort(FORMULA_SUPPORT_COMPARATOR); + } + + String getCoefficients(); + + void setCoefficients(String coefficients); + + Map<String, Double> getCoefficientValues(); + + String getFormulaOneVariableName(); + + String getFormulaTwoVariableName(); + + void setStartDate(Date startDate); + + void setEndDate(Date endDate); + + String getSource(); + + void setSource(String source); + + default Set<String> getCoefficientNames() { + return getCoefficientValues().keySet(); + } + + String getFormulaOne(); + + String getFormulaTwo(); + + default void revalidateFormulaOne() { + boolean result = FormulaHelper.validateRelation(this, getFormulaOne(), getFormulaOneVariableName()); + setFormulaOneValid(result); + } + + boolean isFormulaOneValid(); + + void setFormulaOneValid(boolean formulaOneValid); + + boolean isFormulaTwoValid(); + + void setFormulaTwoValid(boolean formulaTwoValid); + + default void revalidateFormulaTwo() { + boolean result = FormulaHelper.validateRelation(this, getFormulaTwo(), getFormulaTwoVariableName()); + setFormulaTwoValid(result); + } + + default Double getCoefficientValue(String coefficientName) { + return getCoefficientValues().get(coefficientName); + } + + default Float computeFromFormulaOne(float data) { + // only used for entities + return null; + } + + default Float computeFromFormulaTwo(float data) { + // only used for entities + return null; + } + +} ===================================== core/api/dto/src/main/java/fr/ird/observe/dto/referential/WithSpeciesFaoCode.java ===================================== @@ -0,0 +1,43 @@ +package fr.ird.observe.dto.referential; + +/*- + * #%L + * ObServe Core :: API :: Dto + * %% + * Copyright (C) 2008 - 2023 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.dto.CommonDto; +import fr.ird.observe.dto.DtoAndReferenceAware; + +/** + * Created on 18/10/2022. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.0.14 + */ +public interface WithSpeciesFaoCode extends DtoAndReferenceAware { + + CommonDto getSpecies(); + + default CommonDto getSpeciesLabel() { + return getSpecies(); + } + + String getSpeciesFaoCode(); +} ===================================== core/api/dto/src/test/java/fr/ird/observe/dto/referential/FormulaHelperTest.java ===================================== @@ -0,0 +1,152 @@ +package fr.ird.observe.dto.referential; + +/*- + * #%L + * ObServe Core :: API :: Dto + * %% + * Copyright (C) 2008 - 2024 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.dto.CommonDto; +import io.ultreia.java4all.lang.Numbers; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Date; +import java.util.Map; + +/** + * Created on 05/11/16. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 6.0 + */ +public class FormulaHelperTest { + + @Test + public void testComputeValue() { + + WithFormula formula = new WithFormula() { + @Override + public String getSpeciesFaoCode() { + return null; + } + + @Override + public CommonDto getSpecies() { + return null; + } + + @Override + public Date getStartDate() { + return null; + } + + @Override + public Date getEndDate() { + return null; + } + + @Override + public String getCoefficients() { + return "a=3.8e-5:b=2.78 "; + } + + @Override + public void setCoefficients(String coefficients) { + + } + + @Override + public Double getCoefficientValue(String coefficientName) { + return getCoefficientValues().get(coefficientName); + } + + @Override + public Map<String, Double> getCoefficientValues() { + return Map.of("a", 3.8e-5, "b", 2.78); + } + + @Override + public String getFormulaOneVariableName() { + return null; + } + + @Override + public String getFormulaTwoVariableName() { + return null; + } + + @Override + public void setStartDate(Date startDate) { + + } + + @Override + public void setEndDate(Date endDate) { + + } + + @Override + public String getSource() { + return null; + } + + @Override + public void setSource(String source) { + + } + + @Override + public String getFormulaOne() { + return null; + } + + @Override + public String getFormulaTwo() { + return null; + } + + @Override + public boolean isFormulaOneValid() { + return false; + } + + @Override + public void setFormulaOneValid(boolean formulaOneValid) { + + } + + @Override + public boolean isFormulaTwoValid() { + return false; + } + + @Override + public void setFormulaTwoValid(boolean formulaTwoValid) { + + } + }; + + Float weight = FormulaHelper.computeValue(formula, "a * Math.pow(L, b)", null, "L", 84.0f, Numbers::roundThreeDigits); + Assert.assertNotNull(weight); + + float excepted = (float) (Math.pow(84.0, 2.78) * 3.8e-5); + Assert.assertEquals(excepted, weight, 3); + } +} View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/commit/dada0ce77b548dc5dda208af5b... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/commit/dada0ce77b548dc5dda208af5b... You're receiving this email because of your account on gitlab.com.
participants (1)
-
Tony CHEMIT (@tchemit)