From 65924fe5babeacc4a5d1592d4d61ef4200d18e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20S=C3=A9nave?= <59770457+nsenave@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:37:25 +0100 Subject: [PATCH] fix: roundabout with except (#1179) * test: add test questionnaire * fix: roundabout with occurrence filter and control * chore: version 3.31.2 --- build.gradle.kts | 4 +- .../in/steps/ddi/DDIInsertControls.java | 2 +- .../in/steps/ddi/DDIMarkRowControls.java | 55 +- .../in/steps/ddi/DDIInsertControlsTest.java | 17 + .../in/steps/ddi/DDIMarkRowControlsTest.java | 22 + .../lunatic/LunaticRoundaboutLoopsTest.java | 46 + .../integration/ddi/ddi-roundabout-except.xml | 945 ++++++++++++++++++ .../pogues/pogues-roundabout-except.json | 404 ++++++++ 8 files changed, 1487 insertions(+), 8 deletions(-) create mode 100644 eno-core/src/test/resources/integration/ddi/ddi-roundabout-except.xml create mode 100644 eno-core/src/test/resources/integration/pogues/pogues-roundabout-except.json diff --git a/build.gradle.kts b/build.gradle.kts index 69785baae..78e01d151 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ java { allprojects { group = "fr.insee.eno" - version = "3.31.1" + version = "3.31.2" } subprojects { @@ -34,7 +34,7 @@ subprojects { sonar { properties { - // The Jacoco coverage report is aggreated in the eno-ws module + // The Jacoco coverage report is aggregated in the eno-ws module val codeCoveragePath = "$projectDir/eno-ws/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml" println("Aggregated code coverage report location:$codeCoveragePath") property("sonar.coverage.jacoco.xmlReportPaths", codeCoveragePath) diff --git a/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControls.java b/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControls.java index 65d33fbde..2a9441c58 100644 --- a/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControls.java +++ b/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControls.java @@ -107,7 +107,7 @@ private static void insertRowLevelControls(RoundaboutSequence roundaboutSequence throw new MappingException("Cannot find loop '" + roundaboutSequence.getLoopReference() + "' " + "referenced in roundabout sequence '" + roundaboutSequence.getId() + "'"); // Insert all the controls that are referenced in the loop in the roundabout sequence object - DDIMarkRowControls.controlReferencesStream(roundaboutLoop.get()) + DDIMarkRowControls.getLoopControlReferences(roundaboutLoop.get(), enoQuestionnaire) .forEach(itemReference -> roundaboutSequence.getControls().add(controlMap.get(itemReference.getId()))); } diff --git a/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControls.java b/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControls.java index 9bc995568..f7b8fc205 100644 --- a/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControls.java +++ b/eno-core/src/main/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControls.java @@ -1,12 +1,15 @@ package fr.insee.eno.core.processing.in.steps.ddi; +import fr.insee.eno.core.exceptions.technical.MappingException; import fr.insee.eno.core.model.EnoQuestionnaire; import fr.insee.eno.core.model.navigation.Control; +import fr.insee.eno.core.model.navigation.Filter; import fr.insee.eno.core.model.navigation.Loop; import fr.insee.eno.core.model.sequence.ItemReference; import fr.insee.eno.core.processing.ProcessingStep; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -22,19 +25,61 @@ public class DDIMarkRowControls implements ProcessingStep { public void apply(EnoQuestionnaire enoQuestionnaire) { // index controls by id Map controls = mapQuestionnaireControls(enoQuestionnaire); - // iterate on loop control references, to set the context of corresponding controls as "row" - enoQuestionnaire.getLoops().forEach(loop -> controlReferencesStream(loop) - .forEach(itemReference -> controls.get(itemReference.getId()).setContext(Control.Context.ROW)) - ); + // iterate on each loop controls and mark the as "row" controls + enoQuestionnaire.getLoops().forEach(loop -> + markControlsAsRow(getLoopControlReferences(loop, enoQuestionnaire), controls)); } + /** + * Method to index questionnaire's controls to get them easily when needed. + * @param enoQuestionnaire Eno questionnaire. + * @return A map of controls indexed by id. + */ static Map mapQuestionnaireControls(EnoQuestionnaire enoQuestionnaire) { return enoQuestionnaire.getControls().stream().collect(Collectors.toMap(Control::getId, control -> control)); } - static Stream controlReferencesStream(Loop loop) { + /** + * Direct search method for filters, indexing seems not useful. + * @param enoQuestionnaire Eno questionnaire. + * @param filterId Identifier of a filter. + * @return The corresponding filter object. + * @throws MappingException if filter object is not found. + */ + private static Filter getFilterById(EnoQuestionnaire enoQuestionnaire, String filterId) { + return enoQuestionnaire.getFilters().stream() + .filter(filter -> filterId.equals(filter.getId())) + .findAny() + .orElseThrow(() -> new MappingException("Didn't find filter object with id " + filterId)); + } + + private static void markControlsAsRow(Stream controlReferences, Map controls) { + controlReferences.forEach(itemReference -> controls.get(itemReference.getId()).setContext(Control.Context.ROW)); + } + + static Stream getLoopControlReferences(Loop loop, EnoQuestionnaire enoQuestionnaire) { + // if the loop contains an occurrence filter, return the control references of that filter + Optional filterReference = loop.getLoopItems().stream() + .filter(itemReference -> ItemReference.ItemType.FILTER.equals(itemReference.getType())) + .findAny(); + if (filterReference.isPresent()) { + Filter filter = getFilterById(enoQuestionnaire, filterReference.get().getId()); + return controlReferencesStream(filter); + } + // otherwise return the loop control references + return controlReferencesStream(loop); + } + + // Note: we could add an interface over loop and filter for the items/scope properties + + private static Stream controlReferencesStream(Loop loop) { return loop.getLoopItems().stream() .filter(itemReference -> ItemReference.ItemType.CONTROL.equals(itemReference.getType())); } + private static Stream controlReferencesStream(Filter filter) { + return filter.getFilterItems().stream() + .filter(itemReference -> ItemReference.ItemType.CONTROL.equals(itemReference.getType())); + } + } diff --git a/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControlsTest.java b/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControlsTest.java index 66399b3e5..21756599f 100644 --- a/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControlsTest.java +++ b/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIInsertControlsTest.java @@ -75,4 +75,21 @@ void questionnaireWithRoundabout() throws DDIParsingException { assertEquals(2, roundaboutSequence.getControls().size()); } + @Test + void questionnaireWithRoundaboutWithOccurrenceFilter() throws DDIParsingException { + // + EnoQuestionnaire enoQuestionnaire = new EnoQuestionnaire(); + DDIMapper ddiMapper = new DDIMapper(); + ddiMapper.mapDDI( + DDIDeserializer.deserialize(this.getClass().getClassLoader().getResourceAsStream( + "integration/ddi/ddi-roundabout-except.xml")), + enoQuestionnaire); + + // + new DDIInsertControls().apply(enoQuestionnaire); + + // + RoundaboutSequence roundaboutSequence = enoQuestionnaire.getRoundaboutSequences().getFirst(); + assertEquals(1, roundaboutSequence.getControls().size()); + } } diff --git a/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControlsTest.java b/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControlsTest.java index e20c5b1cc..d64e6e960 100644 --- a/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControlsTest.java +++ b/eno-core/src/test/java/fr/insee/eno/core/processing/in/steps/ddi/DDIMarkRowControlsTest.java @@ -38,4 +38,26 @@ void roundaboutLevelControls() throws DDIParsingException { )); } + /** When a roundabout has an occurrence filter, it has an intermediate reference to a filter object that references + * the controls that belong to the loop. */ + @Test + void roundaboutLevelControlWithFilter() throws DDIParsingException { + // Given + EnoQuestionnaire enoQuestionnaire = new EnoQuestionnaire(); + DDIInstanceDocument ddiInstance = DDIDeserializer.deserialize( + this.getClass().getClassLoader().getResourceAsStream( + "integration/ddi/ddi-roundabout-except.xml")); + // + DDIMapper ddiMapper = new DDIMapper(); + ddiMapper.mapDDI(ddiInstance, enoQuestionnaire); + + // When + new DDIMarkRowControls().apply(enoQuestionnaire); + + // Then + // The questionnaire should have two controls (that are created for a roundabout) + assertEquals(1, enoQuestionnaire.getControls().size()); + assertEquals(Control.Context.ROW, enoQuestionnaire.getControls().getFirst().getContext()); + } + } 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 40b923259..49f1f8f9e 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 @@ -252,4 +252,50 @@ void occurrenceControlTest() { } } + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class RoundaboutWithExcept { + private Roundabout roundabout; + + @BeforeAll + void ddiToLunatic() throws DDIParsingException { + // + EnoParameters parameters = EnoParameters.of( + EnoParameters.Context.HOUSEHOLD, EnoParameters.ModeParameter.CAWI, Format.LUNATIC); + // + Questionnaire lunaticQuestionnaire = DDIToLunatic.transform( + this.getClass().getClassLoader().getResourceAsStream( + "integration/ddi/ddi-roundabout-except.xml"), + parameters); + // the questionnaire should have 1 roundabout component + List roundabouts = lunaticQuestionnaire.getComponents().stream() + .filter(Roundabout.class::isInstance).map(Roundabout.class::cast) + .toList(); + assertEquals(1, roundabouts.size()); + roundabout = roundabouts.getFirst(); + } + + /** The "except" field in Pogues corresponds to the "disabled" expression in Lunatic. */ + @Test + void disabledCondition() { + assertEquals("not(not(Q1 = \"foo\"))", roundabout.getItem().getDisabled().getValue()); + assertEquals(LabelTypeEnum.VTL, roundabout.getItem().getDisabled().getType()); + } + + /** The DDI modeling describes both disabled condition and occurrence-level controls as control objects, + * hence this test. */ + @Test + void occurrenceControl_shouldBePresent() { + Optional rowControl = roundabout.getControls().stream() + .filter(control -> ControlContextType.ROW.equals(control.getType())).findAny(); + assertTrue(rowControl.isPresent()); + assertEquals(ControlCriticalityEnum.INFO, rowControl.get().getCriticality()); + assertEquals("not(true)", rowControl.get().getControl().getValue()); + assertEquals(LabelTypeEnum.VTL, rowControl.get().getControl().getType()); + assertEquals("\"This control should always be displayed.\"", + rowControl.get().getErrorMessage().getValue()); + assertEquals(LabelTypeEnum.VTL_MD, rowControl.get().getErrorMessage().getType()); + } + } + } diff --git a/eno-core/src/test/resources/integration/ddi/ddi-roundabout-except.xml b/eno-core/src/test/resources/integration/ddi/ddi-roundabout-except.xml new file mode 100644 index 000000000..477837ca8 --- /dev/null +++ b/eno-core/src/test/resources/integration/ddi/ddi-roundabout-except.xml @@ -0,0 +1,945 @@ + + + fr.insee + INSEE-m4wyrh7u + 1 + + + Eno - Roundabout with except + + + + fr.insee + RessourcePackage-m4wyrh7u + 1 + + fr.insee + InterviewerInstructionScheme-m4wyrh7u + 1 + + A définir + + + fr.insee + m4wzx3n4-OL + 1 + + loop.instanceLabel + + + + "Q1 answer: " || ¤m4wz5idy-QOP-m4x0f49y¤ + + + + fr.insee + m4wz5idy-QOP-m4x0f49y + 1 + OutParameter + + + + + + fr.insee + m4wzx3n4-CI-0-II-0 + 1 + + warning + + + + "This control should always be displayed." + + + + + + fr.insee + ControlConstructScheme-m4wyrh7u + 1 + + fr.insee + Sequence-m4wyrh7u + 1 + + Eno - Roundabout with except + + template + + fr.insee + m4wyulxj + 1 + Sequence + + + fr.insee + m4wzsp2v + 1 + Sequence + + + + fr.insee + m4wyulxj + 1 + + S1 + + + "Sequence 1" + + module + + fr.insee + m4wz0l23-QC + 1 + QuestionConstruct + + + fr.insee + m4wzlmjg + 1 + Loop + + + fr.insee + m4wzx3n4 + 1 + Sequence + + + + fr.insee + m4wzayl0 + 1 + + SS1 + + + "First loop sub-sequence" + + submodule + + fr.insee + m4wz5idy-QC + 1 + QuestionConstruct + + + + fr.insee + m4wz6vyr + 1 + + SS2 + + + "Roundabout sub-sequence" + + submodule + + fr.insee + m4wz9i1c-QC + 1 + QuestionConstruct + + + + fr.insee + m4wzsp2v + 1 + + S_LAST + + + "Last sequence" + + module + + fr.insee + m4wzwz9m-QC + 1 + QuestionConstruct + + + + fr.insee + m4wzx3n4 + 1 + + "Roundabout" + + + fr.insee + m4wzx3n4-OL + 1 + Instruction + + roundabout + + fr.insee + m4wzx3n4-LOOP_SS2 + 1 + Loop + + + + fr.insee + m4wzlmjg + 1 + + LOOP_SS1 + + + + vtl + + fr.insee + m4wzlmjg-MIN-IP-1 + 1 + + HOW_MANY + + + + + fr.insee + m4wz0l23-QOP-m4x0m19z + 1 + OutParameter + + + fr.insee + m4wzlmjg-MIN-IP-1 + 1 + InParameter + + + m4wzlmjg-MIN-IP-1 + + + + + vtl + + fr.insee + m4wzlmjg-IP-1 + 1 + + HOW_MANY + + + + + fr.insee + m4wz0l23-QOP-m4x0m19z + 1 + OutParameter + + + fr.insee + m4wzlmjg-IP-1 + 1 + InParameter + + + m4wzlmjg-IP-1 + + + + + vtl + 1 + + + + fr.insee + m4wzlmjg-SEQ + 1 + Sequence + + + + fr.insee + m4wzlmjg-SEQ + 1 + loopContent + + fr.insee + m4wzayl0 + 1 + Sequence + + + + fr.insee + m4wzx3n4-LOOP_SS2 + 1 + + LOOP_SS2 + + + fr.insee + m4wzx3n4-LOOP_SS2-ITE + 1 + IfThenElse + + + + fr.insee + m4wzx3n4-LOOP_SS2-ITE + 1 + + A définir + + + + + hideable + + + vtl + + fr.insee + m4wzx3n4-LOOP_SS2-ITE-IP-1 + 1 + + Q1 + + + + + fr.insee + m4wz5idy-QOP-m4x0f49y + 1 + OutParameter + + + fr.insee + m4wzx3n4-LOOP_SS2-ITE-IP-1 + 1 + InParameter + + + not(m4wzx3n4-LOOP_SS2-ITE-IP-1 = "foo") + + + + fr.insee + m4wzx3n4-LOOP_SS2-ITE-THEN + 1 + Sequence + + + + fr.insee + m4wzx3n4-LOOP_SS2-ITE-THEN + 1 + + + + filteredLoopContent + + fr.insee + m4wz6vyr + 1 + Sequence + + + fr.insee + m4wzx3n4-CI-0 + 1 + ComputationItem + + + + fr.insee + m4wz0l23-QC + 1 + + HOW_MANY + + + fr.insee + m4wz0l23 + 1 + QuestionItem + + + + fr.insee + m4wz5idy-QC + 1 + + Q1 + + + fr.insee + m4wz5idy + 1 + QuestionItem + + + + fr.insee + m4wz9i1c-QC + 1 + + Q2 + + + fr.insee + m4wz9i1c + 1 + QuestionItem + + + + fr.insee + m4wzwz9m-QC + 1 + + Q_LAST + + + fr.insee + m4wzwz9m + 1 + QuestionItem + + + + fr.insee + m4wzx3n4-CI-0 + 1 + + "Occurrence level control" + + + "Occurrence level control" + + + fr.insee + m4wzx3n4-CI-0-II-0 + 1 + Instruction + + informational + + + vtl + true + + + + + + fr.insee + QuestionScheme-m4wyrh7u + 1 + + A définir + + + fr.insee + m4wz0l23 + 1 + + HOW_MANY + + + fr.insee + m4wz0l23-QOP-m4x0m19z + 1 + + HOW_MANY + + + + + fr.insee + m4wz0l23-RDOP-m4x0m19z + 1 + OutParameter + + + fr.insee + m4wz0l23-QOP-m4x0m19z + 1 + OutParameter + + + + + "How many occurrences in the loop?" + + + + + 1 + 10 + + Decimal + + fr.insee + m4wz0l23-RDOP-m4x0m19z + 1 + + + + + fr.insee + m4wz5idy + 1 + + Q1 + + + fr.insee + m4wz5idy-QOP-m4x0f49y + 1 + + Q1 + + + + + fr.insee + m4wz5idy-RDOP-m4x0f49y + 1 + OutParameter + + + fr.insee + m4wz5idy-QOP-m4x0f49y + 1 + OutParameter + + + + + "Question 1" + + + + + fr.insee + m4wz5idy-RDOP-m4x0f49y + 1 + + + + + + fr.insee + m4wz9i1c + 1 + + Q2 + + + fr.insee + m4wz9i1c-QOP-m4x0p3kr + 1 + + Q2 + + + + + fr.insee + m4wz9i1c-RDOP-m4x0p3kr + 1 + OutParameter + + + fr.insee + m4wz9i1c-QOP-m4x0p3kr + 1 + OutParameter + + + + + "Question 2" + + + + + fr.insee + m4wz9i1c-RDOP-m4x0p3kr + 1 + + + + + + fr.insee + m4wzwz9m + 1 + + Q_LAST + + + fr.insee + m4wzwz9m-QOP-m4x0od9z + 1 + + Q_LAST + + + + + fr.insee + m4wzwz9m-RDOP-m4x0od9z + 1 + OutParameter + + + fr.insee + m4wzwz9m-QOP-m4x0od9z + 1 + OutParameter + + + + + "Last question" + + + + + fr.insee + m4wzwz9m-RDOP-m4x0od9z + 1 + + + + + + + fr.insee + CategoryScheme-m4wyrh7u + 1 + + A définir + + + fr.insee + INSEE-COMMUN-CA-Booleen-1 + 1 + + + + + + + fr.insee + ENO_ROUNDABOUT_EXCEPT-CLS + 1 + + ENO_ROUNDABOUT_EXCEPT + + + fr.insee + INSEE-COMMUN-CL-Booleen + 1 + + Booleen + + Regular + + Ordinal + + + fr.insee + INSEE-COMMUN-CL-Booleen-1 + 1 + + fr.insee + INSEE-COMMUN-CA-Booleen-1 + 1 + Category + + 1 + + + + + fr.insee + VariableScheme-m4wyrh7u + 1 + + Variable Scheme for the survey + + + fr.insee + m4wz9mxx + 1 + + HOW_MANY + + + HOW_MANY label + + + fr.insee + m4wz0l23-QOP-m4x0m19z + 1 + OutParameter + + + fr.insee + m4wz0l23 + 1 + QuestionItem + + + + + 1 + 10 + + Decimal + + + + + fr.insee + m4wz2t1o + 1 + + Q1 + + + Q1 label + + + fr.insee + m4wz5idy-QOP-m4x0f49y + 1 + OutParameter + + + fr.insee + m4wz5idy + 1 + QuestionItem + + + + + + + fr.insee + m4wytrud + 1 + + Q2 + + + Q2 label + + + fr.insee + m4wz9i1c-QOP-m4x0p3kr + 1 + OutParameter + + + fr.insee + m4wz9i1c + 1 + QuestionItem + + + + + + + fr.insee + m4wzqf98 + 1 + + Q_LAST + + + Q_LAST label + + + fr.insee + m4wzwz9m-QOP-m4x0od9z + 1 + OutParameter + + + fr.insee + m4wzwz9m + 1 + QuestionItem + + + + + + + fr.insee + m4wzlmjg-vg + 1 + + + fr.insee + m4wzlmjg + 1 + Loop + + + fr.insee + m4wzx3n4-LOOP_SS2 + 1 + Loop + + + Loop + + LOOP_SS1 + + + fr.insee + m4wz2t1o + 1 + Variable + + + fr.insee + m4wytrud + 1 + Variable + + + + fr.insee + INSEE-Instrument-m4wyrh7u-vg + 1 + + + fr.insee + Instrument-m4wyrh7u + 1 + Instrument + + + Questionnaire + + ENO_ROUNDABOUT_EXCEPT + + + fr.insee + m4wz9mxx + 1 + Variable + + + fr.insee + m4wzqf98 + 1 + Variable + + + fr.insee + m4wzlmjg-vg + 1 + VariableGroup + + + + + fr.insee + INSEE-SIMPSONS-PIS-1 + 1 + + SIMPSONS + + + Processing instructions of the Simpsons questionnaire + + + + fr.insee + INSEE-SIMPSONS-MRS + 1 + + Liste de formats numériques et dates de + l'enquête + Numeric and DateTime list for the survey + + + + + fr.insee + StudyUnit-m4wyrh7u + 1 + + + fr.insee + DataCollection-m4wyrh7u + 1 + + fr.insee + QuestionScheme-m4wyrh7u + 1 + QuestionScheme + + + fr.insee + ControlConstructScheme-m4wyrh7u + 1 + ControlConstructScheme + + + fr.insee + InterviewerInstructionScheme-m4wyrh7u + 1 + InterviewerInstructionScheme + + + fr.insee + InstrumentScheme-m4wyrh7u + 1 + + fr.insee + Instrument-m4wyrh7u + 1 + + ENO_ROUNDABOUT_EXCEPT + + + Eno - Roundabout with except questionnaire + + A définir + + fr.insee + Sequence-m4wyrh7u + 1 + Sequence + + + + + + diff --git a/eno-core/src/test/resources/integration/pogues/pogues-roundabout-except.json b/eno-core/src/test/resources/integration/pogues/pogues-roundabout-except.json new file mode 100644 index 000000000..253574b3a --- /dev/null +++ b/eno-core/src/test/resources/integration/pogues/pogues-roundabout-except.json @@ -0,0 +1,404 @@ +{ + "id": "m4wyrh7u", + "Name": "ENO_ROUNDABOUT_EXCEPT", + "Child": [ + { + "id": "m4wyulxj", + "Name": "S1", + "type": "SequenceType", + "Child": [ + { + "id": "m4wz0l23", + "Name": "HOW_MANY", + "type": "QuestionType", + "Label": [ + "\"How many occurrences in the loop?\"" + ], + "depth": 2, + "Control": [], + "Response": [ + { + "id": "m4x0lw4j", + "Datatype": { + "Unit": "", + "type": "NumericDatatypeType", + "Maximum": "10", + "Minimum": "1", + "Decimals": "", + "typeName": "NUMERIC", + "IsDynamicUnit": false + }, + "mandatory": false, + "CollectedVariableReference": "m4wz9mxx" + } + ], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "questionType": "SIMPLE" + }, + { + "id": "m4wzayl0", + "Name": "SS1", + "type": "SequenceType", + "Child": [ + { + "id": "m4wz5idy", + "Name": "Q1", + "type": "QuestionType", + "Label": [ + "\"Question 1\"" + ], + "depth": 3, + "Control": [], + "Response": [ + { + "id": "m4x0c7gc", + "Datatype": { + "type": "TextDatatypeType", + "Pattern": "", + "typeName": "TEXT", + "MaxLength": 249 + }, + "mandatory": false, + "CollectedVariableReference": "m4wz2t1o" + } + ], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "questionType": "SIMPLE" + } + ], + "Label": [ + "\"First loop sub-sequence\"" + ], + "depth": 2, + "Control": [], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "genericName": "SUBMODULE" + }, + { + "id": "m4wzx3n4", + "Loop": { + "Name": "LOOP_SS2", + "Filter": "$Q1$ = \"foo\"", + "MemberReference": [ + "m4wz6vyr", + "m4wz6vyr" + ], + "IterableReference": "m4wzlmjg" + }, + "Name": "ROUNDABOUT_SS2", + "type": "RoundaboutType", + "Label": [ + "\"Roundabout\"" + ], + "depth": 2, + "Locked": false, + "Control": [ + { + "id": "m4wzu41c", + "scope": "occurrence", + "criticity": "INFO", + "Expression": "true", + "Description": "\"Occurrence level control\"", + "FailMessage": "\"This control should always be displayed.\"", + "post_collect": false, + "during_collect": false + } + ], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "OccurrenceLabel": "\"Q1 answer: \" || $Q1$", + "OccurrenceDescription": "" + }, + { + "id": "m4wz6vyr", + "Name": "SS2", + "type": "SequenceType", + "Child": [ + { + "id": "m4wz9i1c", + "Name": "Q2", + "type": "QuestionType", + "Label": [ + "\"Question 2\"" + ], + "depth": 3, + "Control": [], + "Response": [ + { + "id": "m4x0bvun", + "Datatype": { + "type": "TextDatatypeType", + "Pattern": "", + "typeName": "TEXT", + "MaxLength": 249 + }, + "mandatory": false, + "CollectedVariableReference": "m4wytrud" + } + ], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "questionType": "SIMPLE" + } + ], + "Label": [ + "\"Roundabout sub-sequence\"" + ], + "depth": 2, + "Control": [], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "genericName": "SUBMODULE" + } + ], + "Label": [ + "\"Sequence 1\"" + ], + "depth": 1, + "Control": [], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "genericName": "MODULE" + }, + { + "id": "m4wzsp2v", + "Name": "S_LAST", + "type": "SequenceType", + "Child": [ + { + "id": "m4wzwz9m", + "Name": "Q_LAST", + "type": "QuestionType", + "Label": [ + "\"Last question\"" + ], + "depth": 2, + "Control": [], + "Response": [ + { + "id": "m4x0sxrf", + "Datatype": { + "type": "TextDatatypeType", + "Pattern": "", + "typeName": "TEXT", + "MaxLength": 249 + }, + "mandatory": false, + "CollectedVariableReference": "m4wzqf98" + } + ], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "questionType": "SIMPLE" + } + ], + "Label": [ + "\"Last sequence\"" + ], + "depth": 1, + "Control": [], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "genericName": "MODULE" + }, + { + "id": "idendquest", + "Name": "QUESTIONNAIRE_END", + "type": "SequenceType", + "Child": [], + "Label": [ + "QUESTIONNAIRE_END" + ], + "depth": 1, + "Control": [], + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "FlowControl": [], + "genericName": "MODULE" + } + ], + "Label": [ + "Eno - Roundabout with except" + ], + "final": false, + "owner": "ENO-INTEGRATION-TESTS", + "agency": "fr.insee", + "CodeLists": { + "CodeList": [] + }, + "Variables": { + "Variable": [ + { + "id": "m4wz9mxx", + "Name": "HOW_MANY", + "type": "CollectedVariableType", + "Label": "HOW_MANY label", + "Datatype": { + "Unit": "", + "type": "NumericDatatypeType", + "Maximum": "10", + "Minimum": "1", + "Decimals": "", + "typeName": "NUMERIC", + "IsDynamicUnit": false + } + }, + { + "id": "m4wz2t1o", + "Name": "Q1", + "type": "CollectedVariableType", + "Label": "Q1 label", + "Scope": "m4wzlmjg", + "Datatype": { + "type": "TextDatatypeType", + "Pattern": "", + "typeName": "TEXT", + "MaxLength": 249 + } + }, + { + "id": "m4wytrud", + "Name": "Q2", + "type": "CollectedVariableType", + "Label": "Q2 label", + "Scope": "m4wzlmjg", + "Datatype": { + "type": "TextDatatypeType", + "Pattern": "", + "typeName": "TEXT", + "MaxLength": 249 + } + }, + { + "id": "m4wzqf98", + "Name": "Q_LAST", + "type": "CollectedVariableType", + "Label": "Q_LAST label", + "Datatype": { + "type": "TextDatatypeType", + "Pattern": "", + "typeName": "TEXT", + "MaxLength": 249 + } + } + ] + }, + "flowLogic": "FILTER", + "Iterations": { + "Iteration": [ + { + "id": "m4wzlmjg", + "Name": "LOOP_SS1", + "Step": "1", + "type": "DynamicIterationType", + "Maximum": "$HOW_MANY$", + "Minimum": "$HOW_MANY$", + "MemberReference": [ + "m4wzayl0", + "m4wzayl0" + ] + } + ] + }, + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "FlowControl": [], + "genericName": "QUESTIONNAIRE", + "ComponentGroup": [ + { + "id": "m4x0t4ws", + "Name": "PAGE_1", + "Label": [ + "Components for page 1" + ], + "MemberReference": [ + "idendquest", + "m4wyulxj", + "m4wz0l23", + "m4wzayl0", + "m4wz5idy", + "m4wzx3n4", + "m4wz6vyr", + "m4wz9i1c", + "m4wzsp2v", + "m4wzwz9m" + ] + } + ], + "DataCollection": [ + { + "id": "s2106-dc", + "uri": "http://ddi:fr.insee:DataCollection.s2106-dc", + "Name": "Enquête capacité à innover et stratégie 2022" + } + ], + "lastUpdatedDate": "Fri Dec 20 2024 18:10:40 GMT+0100 (heure normale d’Europe centrale)", + "formulasLanguage": "VTL", + "childQuestionnaireRef": [] +} \ No newline at end of file