From 608fcac2f487d6f1a05ebbfc3f3067f868dc34ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20S=C3=A9nave?= <59770457+nsenave@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:14:36 +0100 Subject: [PATCH] fix: roundabout controls (#1150) * fix(roundabout): reverse controls in lunatic * fix(roundabout): locked property when controls are present * chore: bump version --- build.gradle.kts | 2 +- .../model/sequence/RoundaboutSequence.java | 18 +++++++++++++++++- .../LunaticReverseConsistencyControlLabel.java | 4 +++- .../steps/lunatic/LunaticRoundaboutLoops.java | 4 ++++ .../lunatic/LunaticRoundaboutLoopsTest.java | 9 +++++++-- 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d44dfb30e..b47297b05 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ java { allprojects { group = "fr.insee.eno" - version = "3.28.0-SNAPSHOT" + version = "3.28.0-SNAPSHOT.1" } subprojects { diff --git a/eno-core/src/main/java/fr/insee/eno/core/model/sequence/RoundaboutSequence.java b/eno-core/src/main/java/fr/insee/eno/core/model/sequence/RoundaboutSequence.java index d01504f6c..17322d46c 100644 --- a/eno-core/src/main/java/fr/insee/eno/core/model/sequence/RoundaboutSequence.java +++ b/eno-core/src/main/java/fr/insee/eno/core/model/sequence/RoundaboutSequence.java @@ -1,15 +1,18 @@ package fr.insee.eno.core.model.sequence; +import fr.insee.ddi.lifecycle33.datacollection.ComputationItemType; import fr.insee.ddi.lifecycle33.datacollection.SequenceType; import fr.insee.eno.core.annotations.Contexts.Context; import fr.insee.eno.core.annotations.DDI; import fr.insee.eno.core.model.navigation.Control; import fr.insee.eno.core.parameter.Format; +import fr.insee.eno.core.reference.DDIIndex; import lombok.Getter; import lombok.Setter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Special kind of sequence that describes a "roundabout". @@ -22,6 +25,8 @@ @Context(format = Format.DDI, type = SequenceType.class) public class RoundaboutSequence extends AbstractSequence { + private static final String DDI_LOCKED_CONTROL_TYPE = "roundabout-locked"; + /** DDI reference of the loop. * Note: mapped as the id of the first control construct reference. */ @DDI("getControlConstructReferenceArray(0).getIDArray(0).getStringValue()") @@ -29,7 +34,7 @@ public class RoundaboutSequence extends AbstractSequence { /** Boolean that describes if the completed occurrences should be locked or not. * In DDI, this is modeled by the presence or not of a ComputationItem among the control construct references. */ - @DDI("!getControlConstructReferenceList().?[#this.getTypeOfObject().toString() == 'ComputationItem'].isEmpty()") + @DDI("T(fr.insee.eno.core.model.sequence.RoundaboutSequence).ddiLockedProperty(#this, #index)") private Boolean locked; /** @@ -41,4 +46,15 @@ public class RoundaboutSequence extends AbstractSequence { */ private List controls = new ArrayList<>(); + public static boolean ddiLockedProperty(SequenceType ddiRoundaboutSequence, DDIIndex ddiIndex) { + return ddiRoundaboutSequence.getControlConstructReferenceList().stream() + .filter(reference -> "ComputationItem".equals(reference.getTypeOfObject().toString())) + .map(reference -> ddiIndex.get(reference.getIDArray(0).getStringValue())) + .filter(Objects::nonNull) + .filter(ComputationItemType.class::isInstance) + .map(ComputationItemType.class::cast) + .anyMatch(computationItem -> + DDI_LOCKED_CONTROL_TYPE.equals(computationItem.getTypeOfComputationItem().getStringValue())); + } + } diff --git a/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticReverseConsistencyControlLabel.java b/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticReverseConsistencyControlLabel.java index 85dc424ad..134201bd7 100644 --- a/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticReverseConsistencyControlLabel.java +++ b/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticReverseConsistencyControlLabel.java @@ -1,6 +1,7 @@ package fr.insee.eno.core.processing.out.steps.lunatic; import fr.insee.eno.core.processing.ProcessingStep; +import fr.insee.eno.core.utils.VtlSyntaxUtils; import fr.insee.lunatic.model.flat.*; import java.util.Collection; @@ -27,7 +28,7 @@ private void processComponents(List components) { .filter(control -> control.getTypeOfControl().equals(ControlTypeEnum.CONSISTENCY)) .forEach(control -> { LabelType label = control.getControl(); - label.setValue("not(" + label.getValue() + ")"); + label.setValue(VtlSyntaxUtils.invertBooleanExpression(label.getValue())); }); components.stream() @@ -35,4 +36,5 @@ private void processComponents(List components) { .map(ComponentNestingType.class::cast) .forEach(componentNesting -> processComponents(componentNesting.getComponents())); } + } diff --git a/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoops.java b/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoops.java index 80165dba1..da7f5ef86 100644 --- a/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoops.java +++ b/eno-core/src/main/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoops.java @@ -9,6 +9,7 @@ import fr.insee.eno.core.model.navigation.LinkedLoop; import fr.insee.eno.core.model.sequence.RoundaboutSequence; import fr.insee.eno.core.processing.ProcessingStep; +import fr.insee.eno.core.utils.VtlSyntaxUtils; import fr.insee.lunatic.model.flat.*; import fr.insee.lunatic.model.flat.variable.CollectedVariableType; import fr.insee.lunatic.model.flat.variable.CollectedVariableValues; @@ -167,6 +168,9 @@ private Roundabout createRoundabout(RoundaboutSequence roundaboutSequence, Loop ControlType lunaticControl = new ControlType(); lunaticMapper.mapEnoObject(enoControl, lunaticControl); lunaticRoundabout.getControls().add(lunaticControl); + // Control expressions have to be inverted in Lunatic + lunaticControl.getControl().setValue( + VtlSyntaxUtils.invertBooleanExpression(lunaticControl.getControl().getValue())); }); // Roundabout.Item lunaticRoundaboutItem = new Roundabout.Item(); diff --git a/eno-core/src/test/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoopsTest.java b/eno-core/src/test/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoopsTest.java index c18784fc0..abd30f77e 100644 --- a/eno-core/src/test/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoopsTest.java +++ b/eno-core/src/test/java/fr/insee/eno/core/processing/out/steps/lunatic/LunaticRoundaboutLoopsTest.java @@ -209,6 +209,11 @@ void ddiToLunatic() throws DDIParsingException { roundabout = roundabouts.getFirst(); } + @Test + void lockedPropertyTest() { + assertFalse(roundabout.getLocked()); + } + @Test void controlsCount() { assertEquals(2, roundabout.getControls().size()); @@ -221,7 +226,7 @@ void roundaboutControlTest() { .toList(); assertEquals(1, roundaboutControls.size()); ControlType roundaboutControl = roundaboutControls.getFirst(); - assertEquals("count(Q2) < 3", roundaboutControl.getControl().getValue()); + assertEquals("not(count(Q2) < 3)", roundaboutControl.getControl().getValue()); assertEquals(LabelTypeEnum.VTL, roundaboutControl.getControl().getType()); assertEquals("\"There is less than 3 answers in the roundabout.\"", roundaboutControl.getErrorMessage().getValue()); @@ -237,7 +242,7 @@ void occurrenceControlTest() { .toList(); assertEquals(1, occurrenceControls.size()); ControlType occurrenceControl = occurrenceControls.getFirst(); - assertEquals("Q2 = \"bar\"", occurrenceControl.getControl().getValue()); + assertEquals("not(Q2 = \"bar\")", occurrenceControl.getControl().getValue()); assertEquals(LabelTypeEnum.VTL, occurrenceControl.getControl().getType()); assertEquals("\"Occurrence with question 1 = '\" || Q1 || \"' answered 'bar' at question 2.\"", occurrenceControl.getErrorMessage().getValue().stripTrailing());