diff --git a/src/main/java/fr/insee/pogues/exception/DeReferencingException.java b/src/main/java/fr/insee/pogues/exception/DeReferencingException.java new file mode 100644 index 00000000..b109d3bb --- /dev/null +++ b/src/main/java/fr/insee/pogues/exception/DeReferencingException.java @@ -0,0 +1,12 @@ +package fr.insee.pogues.exception; + +/** + * Exception thrown if an error occurs during questionnaire de-referencing (composition feature). + */ +public class DeReferencingException extends Exception { + + public DeReferencingException(String message, Exception e) { + super(message, e); + } + +} diff --git a/src/main/java/fr/insee/pogues/persistence/service/VariablesServiceImpl.java b/src/main/java/fr/insee/pogues/persistence/service/VariablesServiceImpl.java index cdfeb264..7c182295 100644 --- a/src/main/java/fr/insee/pogues/persistence/service/VariablesServiceImpl.java +++ b/src/main/java/fr/insee/pogues/persistence/service/VariablesServiceImpl.java @@ -37,6 +37,12 @@ public class VariablesServiceImpl implements VariablesService { @Autowired private QuestionnairesServiceQuery questionnaireServiceQuery; + public VariablesServiceImpl() {} + + public VariablesServiceImpl(QuestionnairesServiceQuery questionnairesServiceQuery) { + this.questionnaireServiceQuery = questionnairesServiceQuery; + } + public JSONArray getVariablesByQuestionnaireForPublicEnemy(String id){ try { JSONObject questionnaire = questionnaireServiceQuery.getQuestionnaireByID(id); diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/CompositionStep.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/CompositionStep.java new file mode 100644 index 00000000..8be1f25c --- /dev/null +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/CompositionStep.java @@ -0,0 +1,19 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.exception.DeReferencingException; +import fr.insee.pogues.model.Questionnaire; + +/** + * Interface for processing step when de-referencing a questionnaire. + */ +public interface CompositionStep { + + /** + * Update questionnaire content with referenced questionnaire given. + * @param questionnaire Referencing questionnaire. + * @param referencedQuestionnaire Referenced questionnaire. + * @throws DeReferencingException if an error occurs during the de-referencing step. + */ + void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) throws DeReferencingException; + +} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/FilterComposition.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/FilterComposition.java deleted file mode 100644 index eeb59be1..00000000 --- a/src/main/java/fr/insee/pogues/transforms/visualize/composition/FilterComposition.java +++ /dev/null @@ -1,44 +0,0 @@ -package fr.insee.pogues.transforms.visualize.composition; - -import fr.insee.pogues.exception.IllegalFlowControlException; -import fr.insee.pogues.model.ComponentType; -import fr.insee.pogues.model.FlowControlType; -import fr.insee.pogues.model.Questionnaire; - -import java.util.List; - -import static fr.insee.pogues.utils.PoguesModelUtils.getFlowControlBounds; -import static fr.insee.pogues.utils.PoguesModelUtils.getSequences; - -/** - * Methods to insert and update FlowControl (filters) objects when de-referencing a questionnaire. - */ -class FilterComposition { - - private FilterComposition() {} - - /** Replace filter bounds that are reference a questionnaire by its first or last sequence. - * @param referencedQuestionnaire Referenced questionnaire. - * @param flowControlType The FlowControl object to be updated. - * @throws IllegalFlowControlException If the FlowControl 'IfTrue' property doesn't match the format "id-id". - */ - static void updateFlowControlBounds(Questionnaire referencedQuestionnaire, FlowControlType flowControlType) - throws IllegalFlowControlException { - // - String reference = referencedQuestionnaire.getId(); - // - String[] flowControlBounds = getFlowControlBounds(flowControlType); - // Replace questionnaire reference by its first/last sequence - String beginMember = flowControlBounds[0]; - String endMember = flowControlBounds[1]; - if (beginMember.equals(reference)) { - beginMember = referencedQuestionnaire.getChild().get(0).getId(); - } - if (endMember.equals(reference)) { - List referenceSequences = getSequences(referencedQuestionnaire); - endMember = referenceSequences.get(referenceSequences.size() - 1).getId(); - } - flowControlType.setIfTrue(beginMember+"-"+endMember); - } - -} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertCodeLists.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertCodeLists.java new file mode 100644 index 00000000..3c8d3f39 --- /dev/null +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertCodeLists.java @@ -0,0 +1,60 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.CodeLists; +import fr.insee.pogues.model.Questionnaire; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashSet; +import java.util.Set; + +/** + * Implementation of CompositionStep to insert code lists of a referenced questionnaire. + */ +@Slf4j +class InsertCodeLists implements CompositionStep { + + /** Host questionnaire. */ + private Questionnaire questionnaire; + /** Host questionnaire code list names. */ + private final Set codeListNames = new HashSet<>(); + + /** + * Insert code lists of the referenced questionnaire in the referencing questionnaire. + * If a code list of the referenced questionnaire has the same name asa list in the referencing questionnaire, + * the code list is not added. + * @param questionnaire Referencing questionnaire. + * @param referencedQuestionnaire Referenced questionnaire. + */ + @Override + public void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) { + // + this.questionnaire = questionnaire; + // + CodeLists refCodeLists = referencedQuestionnaire.getCodeLists(); + if (refCodeLists != null) { + // + hostCodeLists(); + // + if (questionnaire.getCodeLists() == null) + questionnaire.setCodeLists(new CodeLists()); + // + refCodeLists.getCodeList().forEach(codeList -> { + if (! codeListNames.contains(codeList.getName())) + questionnaire.getCodeLists().getCodeList().add(codeList); + else + log.info("Code list with name '{}' is already in host questionnaire '{}', " + + "so it has not been inserted from reference '{}'", + codeList.getName(), questionnaire.getId(), referencedQuestionnaire.getId()); + }); + log.info("Code lists from '{}' inserted in '{}'", referencedQuestionnaire.getId(), questionnaire.getId()); + } else { + log.info("No code lists in referenced questionnaire '{}'", referencedQuestionnaire.getId()); + } + } + + private void hostCodeLists() { + if (questionnaire.getCodeLists() != null) + questionnaire.getCodeLists().getCodeList().forEach(codeList -> codeListNames.add(codeList.getName())); + } + +} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertFlowControls.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertFlowControls.java new file mode 100644 index 00000000..bb57622e --- /dev/null +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertFlowControls.java @@ -0,0 +1,23 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.Questionnaire; +import lombok.extern.slf4j.Slf4j; + +/** + * Implementation of CompositionStep to insert flow controls of a referenced questionnaire. + */ +@Slf4j +class InsertFlowControls implements CompositionStep { + + /** + * Insert flow controls of the referenced questionnaire in the referencing questionnaire. + * @param questionnaire Referencing questionnaire. + * @param referencedQuestionnaire Referenced questionnaire. + */ + @Override + public void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) { + questionnaire.getFlowControl().addAll(referencedQuestionnaire.getFlowControl()); + log.info("FlowControl from '{}' inserted in '{}'", referencedQuestionnaire.getId(), questionnaire.getId()); + } + +} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertIterations.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertIterations.java new file mode 100644 index 00000000..2791fbec --- /dev/null +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertIterations.java @@ -0,0 +1,30 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.Questionnaire; +import lombok.extern.slf4j.Slf4j; + +/** + * Implementation of CompositionStep to insert iterations of a referenced questionnaire. + */ +@Slf4j +class InsertIterations implements CompositionStep { + + /** + * Insert iterations of the referenced questionnaire in the referencing questionnaire. + * @param questionnaire Referencing questionnaire. + * @param referencedQuestionnaire Referenced questionnaire. + */ + @Override + public void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) { + Questionnaire.Iterations refIterations = referencedQuestionnaire.getIterations(); + if (refIterations != null) { + if (questionnaire.getIterations() == null) + questionnaire.setIterations(new Questionnaire.Iterations()); + questionnaire.getIterations().getIteration().addAll(refIterations.getIteration()); + log.info("Iterations from '{}' inserted in '{}'", referencedQuestionnaire.getId(), questionnaire.getId()); + } else { + log.info("No iterations in referenced questionnaire '{}'", referencedQuestionnaire.getId()); + } + } + +} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/SequenceComposition.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertSequences.java similarity index 74% rename from src/main/java/fr/insee/pogues/transforms/visualize/composition/SequenceComposition.java rename to src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertSequences.java index 46af2bc9..ab68176e 100644 --- a/src/main/java/fr/insee/pogues/transforms/visualize/composition/SequenceComposition.java +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/InsertSequences.java @@ -1,5 +1,6 @@ package fr.insee.pogues.transforms.visualize.composition; +import fr.insee.pogues.exception.DeReferencingException; import fr.insee.pogues.model.ComponentType; import fr.insee.pogues.model.Questionnaire; import lombok.extern.slf4j.Slf4j; @@ -9,19 +10,18 @@ import static fr.insee.pogues.utils.PoguesModelUtils.getSequences; /** - * Methods to insert and update sequence objects when de-referencing a questionnaire. + * Implementation of CompositionStep to replace questionnaire reference by its sequences. */ @Slf4j -class SequenceComposition { - - private SequenceComposition() {} +class InsertSequences implements CompositionStep { /** * Replace questionnaire reference by its sequences. * @param questionnaire Referencing questionnaire. * @param referencedQuestionnaire Referenced questionnaire. */ - static void insertSequences(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) { + @Override + public void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) { // List refSequences = getSequences(referencedQuestionnaire); int indexOfModification = 0; @@ -38,6 +38,8 @@ static void insertSequences(Questionnaire questionnaire, Questionnaire reference for (int i=0; i referenceSequences = getSequences(referencedQuestionnaire); + endMember = referenceSequences.get(referenceSequences.size() - 1).getId(); + } + flowControlType.setIfTrue(beginMember+"-"+endMember); + } + +} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/UpdateIterationBounds.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/UpdateIterationBounds.java new file mode 100644 index 00000000..b9f2f4d3 --- /dev/null +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/UpdateIterationBounds.java @@ -0,0 +1,70 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.exception.DeReferencingException; +import fr.insee.pogues.exception.IllegalIterationException; +import fr.insee.pogues.model.ComponentType; +import fr.insee.pogues.model.IterationType; +import fr.insee.pogues.model.Questionnaire; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +import static fr.insee.pogues.utils.PoguesModelUtils.getIterationBounds; +import static fr.insee.pogues.utils.PoguesModelUtils.getSequences; + +/** + * Implementation of CompositionStep to update Iteration (loops) objects when de-referencing a questionnaire. + */ +@Slf4j +class UpdateIterationBounds implements CompositionStep { + + /** + * Update iterations of the referencing questionnaire: if a start/end member of an iteration is a referenced + * questionnaire, replace the reference id by the right element's id from the referenced questionnaire. + * @param questionnaire Referencing questionnaire. + * @param referencedQuestionnaire Referenced questionnaire. + * @throws DeReferencingException if an error occurs during iterations update. + */ + @Override + public void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) + throws DeReferencingException { + if (questionnaire.getIterations() != null) { + try { + for (IterationType iterationType : questionnaire.getIterations().getIteration()) { + updateIterationBounds(referencedQuestionnaire, iterationType); + } + log.info("Iterations' bounds updated in '{}' when de-referencing '{}'", + questionnaire.getId(), referencedQuestionnaire.getId()); + } catch (IllegalIterationException e) { + String message = String.format( + "Error when updating iteration bounds in questionnaire '%s' with reference '%s'", + questionnaire.getId(), referencedQuestionnaire.getId()); + throw new DeReferencingException(message, e); + } + } + } + + /** Replace loop bounds that reference a questionnaire by its first or last sequence. + * @param referencedQuestionnaire Referenced questionnaire. + * @param iterationType The Iteration object to be updated. + * @throws IllegalIterationException if the 'MemberReference' property in the iteration is invalid. + */ + static void updateIterationBounds(Questionnaire referencedQuestionnaire, IterationType iterationType) + throws IllegalIterationException { + // + String reference = referencedQuestionnaire.getId(); + // + List iterationBounds = getIterationBounds(iterationType); + // Replace questionnaire reference by its first/last sequence + String beginMember = iterationBounds.get(0); + String endMember = iterationBounds.get(1); + if (beginMember.equals(reference)) { + iterationBounds.set(0, referencedQuestionnaire.getChild().get(0).getId()); + } + if (endMember.equals(reference)) { + List referenceSequences = getSequences(referencedQuestionnaire); + iterationBounds.set(1, referenceSequences.get(referenceSequences.size() - 1).getId()); + } + } + +} diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/composition/LoopComposition.java b/src/main/java/fr/insee/pogues/transforms/visualize/composition/UpdateReferencedVariablesScope.java similarity index 75% rename from src/main/java/fr/insee/pogues/transforms/visualize/composition/LoopComposition.java rename to src/main/java/fr/insee/pogues/transforms/visualize/composition/UpdateReferencedVariablesScope.java index f7c244ae..9dc434d0 100644 --- a/src/main/java/fr/insee/pogues/transforms/visualize/composition/LoopComposition.java +++ b/src/main/java/fr/insee/pogues/transforms/visualize/composition/UpdateReferencedVariablesScope.java @@ -1,5 +1,6 @@ package fr.insee.pogues.transforms.visualize.composition; +import fr.insee.pogues.exception.DeReferencingException; import fr.insee.pogues.exception.IllegalIterationException; import fr.insee.pogues.model.*; import lombok.extern.slf4j.Slf4j; @@ -7,45 +8,40 @@ import java.util.List; import static fr.insee.pogues.utils.PoguesModelUtils.getIterationBounds; -import static fr.insee.pogues.utils.PoguesModelUtils.getSequences; /** - * Methods to insert and update Iteration (loop) objects when de-referencing a questionnaire. + * Implementation of CompositionStep to update variable scopes when de-referencing a questionnaire. */ @Slf4j -class LoopComposition { +class UpdateReferencedVariablesScope implements CompositionStep { - private LoopComposition() {} - - /** Replace loop bounds that reference a questionnaire by its first or last sequence. + /** + * If the referenced questionnaire is in an iteration (loop) in referencing questionnaire, + * variables in referenced questionnaire that have null scope have to be updated. + * Warning: This must be done BEFORE replacing referenced questionnaire by its content. + * (Otherwise, we would have to scan every iteration and determine the variables in their scope, + * which would be much more complex.) + * @param questionnaire Referencing questionnaire. * @param referencedQuestionnaire Referenced questionnaire. - * @param iterationType The Iteration object to be updated. - * @throws IllegalIterationException if the 'MemberReference' property in the iteration is invalid. + * @throws DeReferencingException if an error occurs during variable scopes update. */ - static void updateIterationBounds(Questionnaire referencedQuestionnaire, IterationType iterationType) - throws IllegalIterationException { - // - String reference = referencedQuestionnaire.getId(); - // - List iterationBounds = getIterationBounds(iterationType); - // Replace questionnaire reference by its first/last sequence - String beginMember = iterationBounds.get(0); - String endMember = iterationBounds.get(1); - if (beginMember.equals(reference)) { - iterationBounds.set(0, referencedQuestionnaire.getChild().get(0).getId()); - } - if (endMember.equals(reference)) { - List referenceSequences = getSequences(referencedQuestionnaire); - iterationBounds.set(1, referenceSequences.get(referenceSequences.size() - 1).getId()); + @Override + public void apply(Questionnaire questionnaire, Questionnaire referencedQuestionnaire) + throws DeReferencingException { + try { + if (questionnaire.getIterations() != null) + updateReferencedVariablesScope(questionnaire, referencedQuestionnaire); + } catch (IllegalIterationException e) { + String message = String.format( + "Error when updating referenced variables scope in questionnaire '%s' with reference '%s'", + questionnaire.getId(), referencedQuestionnaire.getId()); + throw new DeReferencingException(message, e); } } /** - * If referenced questionnaire is in a loop in referencing questionnaire, - * variables in referenced questionnaire that have null scope have to be updated. - * Warning: This must be done BEFORE replacing referenced questionnaire by its content. - * Otherwise, we would have to scan every iteration and determine the variables in their scope, - * which would be much more complex. + * Iterate on iterations of the referencing questionnaire. + * For each iteration: update variables scope if the referenced questionnaire is in the scope of the iteration. * @param questionnaire Referencing questionnaire. * @param referencedQuestionnaire Referenced questionnaire. * @throws IllegalIterationException If the 'MemberReference' property is not of size 2 diff --git a/src/test/java/fr/insee/pogues/persistence/service/VariablesServiceImplTest.java b/src/test/java/fr/insee/pogues/persistence/service/VariablesServiceImplTest.java new file mode 100644 index 00000000..9347f3a1 --- /dev/null +++ b/src/test/java/fr/insee/pogues/persistence/service/VariablesServiceImplTest.java @@ -0,0 +1,106 @@ +package fr.insee.pogues.persistence.service; + +import fr.insee.pogues.persistence.query.QuestionnairesServiceQuery; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class VariablesServiceImplTest { + + @Test + void getVariables() throws Exception { + // Given + // Read tested questionnaire + URL url = this.getClass().getClassLoader().getResource( + "persistence/VariablesService/l4i3m6qa.json"); + assert url != null; + String stringQuestionnaire = Files.readString(Path.of(url.toURI())); + JSONObject jsonQuestionnaire = (JSONObject) new JSONParser().parse(stringQuestionnaire); + // Mock questionnaire service + QuestionnairesServiceQuery questionnairesServiceQuery = Mockito.mock(QuestionnairesServiceQuery.class); + Mockito.when(questionnairesServiceQuery.getQuestionnaireByID("l4i3m6qa")).thenReturn(jsonQuestionnaire); + + // When + VariablesServiceImpl variablesService = new VariablesServiceImpl(questionnairesServiceQuery); + String result = variablesService.getVariablesByQuestionnaire("l4i3m6qa"); + + // Then + // (quick and dirty tests, the implementation could be refactored to make it more easily testable) + assertNotNull(result); + assertNotEquals("", result); + // Input questionnaire contains all three types of variables, these should be in output + assertTrue(result.contains("CollectedVariableType")); + assertTrue(result.contains("CalculatedVariableType")); + assertTrue(result.contains("ExternalVariableType")); + // Variable names present in input questionnaire, these should be in output + List.of("CAT_VAR_COMP_1", "CAT_VAR_EXT_1", "CAT_Q1", "CAT_Q2", "CAT_Q21", "CAT_Q22", "CAT_Q23") + .forEach(variableName -> assertTrue(result.contains(variableName))); + } + + @Test + void getVariablesForPublicEnemy() throws Exception { + // Given + // Read tested questionnaire + URL url = this.getClass().getClassLoader().getResource( + "persistence/VariablesService/l4i3m6qa.json"); + assert url != null; + String stringQuestionnaire = Files.readString(Path.of(url.toURI())); + JSONObject jsonQuestionnaire = (JSONObject) new JSONParser().parse(stringQuestionnaire); + // Mock questionnaire service + QuestionnairesServiceQuery questionnairesServiceQuery = Mockito.mock(QuestionnairesServiceQuery.class); + Mockito.when(questionnairesServiceQuery.getQuestionnaireByID("l4i3m6qa")).thenReturn(jsonQuestionnaire); + + // When + VariablesServiceImpl variablesService = new VariablesServiceImpl(questionnairesServiceQuery); + JSONArray result = variablesService.getVariablesByQuestionnaireForPublicEnemy("l4i3m6qa"); + + // Then + assertNotNull(result); + assertEquals(7, result.size()); + } + + /** Custom exception with no stack trace (to not pollute log when running tests) to be used in mocking. */ + static class MockedException extends Exception { + public MockedException() { + super("Mocked exception.", null, true, false); + } + } + + @Test + void getVariables_exceptionDuringQuestionnaireQuery_shouldReturnNull() throws Exception { + // Given + QuestionnairesServiceQuery questionnairesServiceQuery = Mockito.mock(QuestionnairesServiceQuery.class); + Mockito.when(questionnairesServiceQuery.getQuestionnaireByID("foo-id")).thenThrow(new MockedException()); + + // When + VariablesServiceImpl variablesService = new VariablesServiceImpl(questionnairesServiceQuery); + String result = variablesService.getVariablesByQuestionnaire("foo-id"); + + // Then + assertNull(result); + } + + @Test + void getVariablesForPublicEnemy_exceptionDuringQuestionnaireQuery_shouldReturnNull() throws Exception { + // Given + QuestionnairesServiceQuery questionnairesServiceQuery = Mockito.mock(QuestionnairesServiceQuery.class); + Mockito.when(questionnairesServiceQuery.getQuestionnaireByID("foo-id")).thenThrow(new MockedException()); + + // When + VariablesServiceImpl variablesService = new VariablesServiceImpl(questionnairesServiceQuery); + JSONArray result = variablesService.getVariablesByQuestionnaireForPublicEnemy("foo-id"); + + // Then + assertNull(result); + } + +} diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/PoguesJSONToPoguesJSONDerefImplTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/PoguesJSONToPoguesJSONDerefImplTest.java index f3a613e5..af5da4b6 100644 --- a/src/test/java/fr/insee/pogues/transforms/visualize/PoguesJSONToPoguesJSONDerefImplTest.java +++ b/src/test/java/fr/insee/pogues/transforms/visualize/PoguesJSONToPoguesJSONDerefImplTest.java @@ -246,7 +246,7 @@ void dereference_updatedScopes() throws Exception { // ----- Using factorized code for the last tests ----- // private final static String TEST_FOLDER = "transforms/PoguesJSONToPoguesJSONDeref/"; - private final ClassLoader classLoader = this.getClass().getClassLoader();; + private final ClassLoader classLoader = this.getClass().getClassLoader(); private QuestionnairesService mockQuestionnaireService( String folderName, List referencedFileNames, List referenceIds) throws Exception { @@ -346,4 +346,35 @@ void dereference_twoReferences() throws Exception { }); } + @Test + void dereference_linkedLoopIssue() throws Exception { + // Given + String folderName = "linked_loop"; + QuestionnairesService questionnairesService = mockQuestionnaireService( + folderName, + List.of("lgyr3utb_referenced.json"), + List.of("lgyr3utb")); + String testedInput = readQuestionnaire(folderName, "lgyr1y6x_host.json"); + + // When + PoguesJSONToPoguesJSONDerefImpl deref = new PoguesJSONToPoguesJSONDerefImpl(questionnairesService); + Questionnaire outQuestionnaire = deref.transformAsQuestionnaire(testedInput); + + // Then + assertNotNull(outQuestionnaire); + assertDoesNotThrow(() -> PoguesSerializer.questionnaireJavaToString(outQuestionnaire)); + // + assertEquals(2, outQuestionnaire.getIterations().getIteration().size()); + // + Optional loop = outQuestionnaire.getIterations().getIteration().stream() + .filter(iterationType -> "LOOP".equals(iterationType.getName())) + .findFirst(); + Optional linkedLoop = outQuestionnaire.getIterations().getIteration().stream() + .filter(iterationType -> "LINKED_LOOP".equals(iterationType.getName())) + .findFirst(); + assertTrue(loop.isPresent()); + assertTrue(linkedLoop.isPresent()); + assertEquals(loop.get().getId(), ((DynamicIterationType) linkedLoop.get()).getIterableReference()); + } + } diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertCodeListsTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertCodeListsTest.java new file mode 100644 index 00000000..1241ddef --- /dev/null +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertCodeListsTest.java @@ -0,0 +1,88 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.CodeList; +import fr.insee.pogues.model.CodeLists; +import fr.insee.pogues.model.Questionnaire; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class InsertCodeListsTest { + + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); + + @BeforeEach + public void createQuestionnaires() { + QuestionnaireCompositionTest.questionnairesContent(questionnaire, referenced1, referenced2); + } + + @Test + void insertReference_codeLists() { + // + CodeList codeList = new CodeList(); + codeList.setId("codes11"); + referenced1.setCodeLists(new CodeLists()); + referenced1.getCodeLists().getCodeList().add(codeList); + // + assertNull(questionnaire.getCodeLists()); + // + InsertCodeLists insertCodeLists = new InsertCodeLists(); + insertCodeLists.apply(questionnaire, referenced1); + // + assertNotNull(questionnaire.getCodeLists()); + assertFalse(questionnaire.getCodeLists().getCodeList().isEmpty()); + assertEquals("codes11", questionnaire.getCodeLists().getCodeList().get(0).getId()); + } + + @Test + void insertCodeList_differentName() { + // + CodeList codeList = new CodeList(); + codeList.setId("codes1"); + codeList.setName("CODE_LIST_A"); + questionnaire.setCodeLists(new CodeLists()); + questionnaire.getCodeLists().getCodeList().add(codeList); + // + CodeList codeListRef = new CodeList(); + codeListRef.setId("codes11"); + codeListRef.setName("CODE_LIST_B"); + referenced1.setCodeLists(new CodeLists()); + referenced1.getCodeLists().getCodeList().add(codeListRef); + // + InsertCodeLists insertCodeLists = new InsertCodeLists(); + insertCodeLists.apply(questionnaire, referenced1); + // + assertNotNull(questionnaire.getCodeLists()); + assertFalse(questionnaire.getCodeLists().getCodeList().isEmpty()); + assertEquals(2, questionnaire.getCodeLists().getCodeList().size()); + } + + @Test + void insertCodeList_sameName() { + // + CodeList codeList = new CodeList(); + codeList.setId("codes1"); + codeList.setName("CODE_LIST_A"); + questionnaire.setCodeLists(new CodeLists()); + questionnaire.getCodeLists().getCodeList().add(codeList); + // + CodeList codeListRef = new CodeList(); + codeListRef.setId("codes11"); + codeListRef.setName("CODE_LIST_A"); + referenced1.setCodeLists(new CodeLists()); + referenced1.getCodeLists().getCodeList().add(codeListRef); + // + InsertCodeLists insertCodeLists = new InsertCodeLists(); + insertCodeLists.apply(questionnaire, referenced1); + // + assertNotNull(questionnaire.getCodeLists()); + assertFalse(questionnaire.getCodeLists().getCodeList().isEmpty()); + assertEquals(1, questionnaire.getCodeLists().getCodeList().size()); + + assertEquals("codes1", questionnaire.getCodeLists().getCodeList().get(0).getId()); + } + +} diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertFlowControlsTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertFlowControlsTest.java new file mode 100644 index 00000000..761d2b2d --- /dev/null +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertFlowControlsTest.java @@ -0,0 +1,39 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.FlowControlType; +import fr.insee.pogues.model.Questionnaire; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class InsertFlowControlsTest { + + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); + + @BeforeEach + public void createQuestionnaires() { + QuestionnaireCompositionTest.questionnairesContent(questionnaire, referenced1, referenced2); + } + + @Test + void insertReference_loopInReferenced() { + // + FlowControlType flowControlType = new FlowControlType(); + flowControlType.setId("filter11"); + flowControlType.setIfTrue("seq11-seq11"); // begin-end member + referenced1.getFlowControl().add(flowControlType); + // + assertTrue(questionnaire.getFlowControl().isEmpty()); + // + InsertFlowControls insertFlowControls = new InsertFlowControls(); + insertFlowControls.apply(questionnaire, referenced1); + // + assertFalse(questionnaire.getFlowControl().isEmpty()); + assertEquals("filter11", questionnaire.getFlowControl().get(0).getId()); + assertEquals("seq11-seq11", questionnaire.getFlowControl().get(0).getIfTrue()); + } + +} diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertIterationsTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertIterationsTest.java new file mode 100644 index 00000000..a6c24e02 --- /dev/null +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertIterationsTest.java @@ -0,0 +1,44 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.DynamicIterationType; +import fr.insee.pogues.model.IterationType; +import fr.insee.pogues.model.Questionnaire; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class InsertIterationsTest { + + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); + + @BeforeEach + public void createQuestionnaires() { + QuestionnaireCompositionTest.questionnairesContent(questionnaire, referenced1, referenced2); + } + + @Test + void insertReference_loopInReferenced() { + // + IterationType iteration = new DynamicIterationType(); + iteration.setId("loop11"); + iteration.getMemberReference().add("seq11"); // begin member + iteration.getMemberReference().add("seq11"); // end member + referenced1.setIterations(new Questionnaire.Iterations()); + referenced1.getIterations().getIteration().add(iteration); + // + assertNull(questionnaire.getIterations()); + // + InsertIterations insertIterations = new InsertIterations(); + insertIterations.apply(questionnaire, referenced1); + // + assertNotNull(questionnaire.getIterations()); + assertFalse(questionnaire.getIterations().getIteration().isEmpty()); + assertEquals("loop11", questionnaire.getIterations().getIteration().get(0).getId()); + assertEquals("seq11", questionnaire.getIterations().getIteration().get(0).getMemberReference().get(0)); + assertEquals("seq11", questionnaire.getIterations().getIteration().get(0).getMemberReference().get(1)); + } + +} diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertSequencesTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertSequencesTest.java new file mode 100644 index 00000000..8d635192 --- /dev/null +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertSequencesTest.java @@ -0,0 +1,34 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.Questionnaire; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class InsertSequencesTest { + + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); + + @BeforeEach + public void createQuestionnaires() { + QuestionnaireCompositionTest.questionnairesContent(questionnaire, referenced1, referenced2); + } + + @Test + void insertReference_sequences() { + assertEquals("ref1", questionnaire.getChild().get(1).getId()); + assertEquals("ref2", questionnaire.getChild().get(2).getId()); + // + InsertSequences insertSequences = new InsertSequences(); + insertSequences.apply(questionnaire, referenced1); + insertSequences.apply(questionnaire, referenced2); + // + assertEquals("seq1", questionnaire.getChild().get(0).getId()); + assertEquals("seq11", questionnaire.getChild().get(1).getId()); + assertEquals("seq21", questionnaire.getChild().get(2).getId()); + } + +} diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertVariablesTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertVariablesTest.java new file mode 100644 index 00000000..3209ed0c --- /dev/null +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/InsertVariablesTest.java @@ -0,0 +1,36 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.model.ExternalVariableType; +import fr.insee.pogues.model.Questionnaire; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class InsertVariablesTest { + + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); + + @BeforeEach + public void createQuestionnaires() { + QuestionnaireCompositionTest.questionnairesContent(questionnaire, referenced1, referenced2); + } + + @Test + void insertReference_variables() { + // + referenced1.getVariables().getVariable().add(new ExternalVariableType()); + referenced2.getVariables().getVariable().add(new ExternalVariableType()); + // + assertEquals(0, questionnaire.getVariables().getVariable().size()); + // + InsertVariables insertVariables = new InsertVariables(); + insertVariables.apply(questionnaire, referenced1); + insertVariables.apply(questionnaire, referenced2); + // + assertEquals(2, questionnaire.getVariables().getVariable().size()); + } + +} diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/QuestionnaireCompositionTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/QuestionnaireCompositionTest.java index 96eedb6c..7835dab2 100644 --- a/src/test/java/fr/insee/pogues/transforms/visualize/composition/QuestionnaireCompositionTest.java +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/QuestionnaireCompositionTest.java @@ -1,7 +1,6 @@ package fr.insee.pogues.transforms.visualize.composition; -import fr.insee.pogues.exception.IllegalFlowControlException; -import fr.insee.pogues.exception.IllegalIterationException; +import fr.insee.pogues.exception.DeReferencingException; import fr.insee.pogues.model.*; import fr.insee.pogues.utils.PoguesModelUtils; import org.junit.jupiter.api.BeforeEach; @@ -13,14 +12,9 @@ class QuestionnaireCompositionTest { - private Questionnaire questionnaire; - private Questionnaire referenced1; - private Questionnaire referenced2; - - @BeforeEach - public void createQuestionnaires() { + static void questionnairesContent( + Questionnaire questionnaire, Questionnaire referenced1, Questionnaire referenced2) { // - referenced1 = new Questionnaire(); referenced1.setId("ref1"); SequenceType sequence11 = new SequenceType(); sequence11.setId("seq11"); @@ -28,7 +22,6 @@ public void createQuestionnaires() { referenced1.getChild().add(sequence11); referenced1.setVariables(new Questionnaire.Variables()); // - referenced2 = new Questionnaire(); referenced2.setId("ref2"); SequenceType sequence21 = new SequenceType(); sequence21.setId("seq21"); @@ -36,7 +29,6 @@ public void createQuestionnaires() { referenced2.getChild().add(sequence21); referenced2.setVariables(new Questionnaire.Variables()); // - questionnaire = new Questionnaire(); questionnaire.setId("id"); SequenceType sequence1 = new SequenceType(); sequence1.setId("seq1"); @@ -45,7 +37,7 @@ public void createQuestionnaires() { questionnaire.getChild().add(referenced1); questionnaire.getChild().add(referenced2); SequenceType sequence2 = new SequenceType(); - sequence2.setId("seq1"); + sequence2.setId("seq2"); sequence2.getChild().add(new QuestionType()); questionnaire.getChild().add(sequence2); SequenceType fakeEndSequence = new SequenceType(); @@ -54,56 +46,17 @@ public void createQuestionnaires() { questionnaire.setVariables(new Questionnaire.Variables()); } - @Test - void insertReference_sequences() throws IllegalIterationException, IllegalFlowControlException { - assertEquals("ref1", questionnaire.getChild().get(1).getId()); - assertEquals("ref2", questionnaire.getChild().get(2).getId()); - // - QuestionnaireComposition.insertReference(questionnaire, referenced1); - QuestionnaireComposition.insertReference(questionnaire, referenced2); - // - assertEquals("seq1", questionnaire.getChild().get(0).getId()); - assertEquals("seq11", questionnaire.getChild().get(1).getId()); - assertEquals("seq21", questionnaire.getChild().get(2).getId()); - } + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); - @Test - void insertReference_variables() throws IllegalIterationException, IllegalFlowControlException { - // - referenced1.getVariables().getVariable().add(new ExternalVariableType()); - referenced2.getVariables().getVariable().add(new ExternalVariableType()); - // - assertEquals(0, questionnaire.getVariables().getVariable().size()); - // - QuestionnaireComposition.insertReference(questionnaire, referenced1); - QuestionnaireComposition.insertReference(questionnaire, referenced2); - // - assertEquals(2, questionnaire.getVariables().getVariable().size()); - } - - @Test - void insertReference_loopInReferenced() throws IllegalIterationException, IllegalFlowControlException { - // - IterationType iteration = new DynamicIterationType(); - iteration.setId("loop11"); - iteration.getMemberReference().add("seq11"); // begin member - iteration.getMemberReference().add("seq11"); // end member - referenced1.setIterations(new Questionnaire.Iterations()); - referenced1.getIterations().getIteration().add(iteration); - // - assertNull(questionnaire.getIterations()); - // - QuestionnaireComposition.insertReference(questionnaire, referenced1); - // - assertNotNull(questionnaire.getIterations()); - assertFalse(questionnaire.getIterations().getIteration().isEmpty()); - assertEquals("loop11", questionnaire.getIterations().getIteration().get(0).getId()); - assertEquals("seq11", questionnaire.getIterations().getIteration().get(0).getMemberReference().get(0)); - assertEquals("seq11", questionnaire.getIterations().getIteration().get(0).getMemberReference().get(1)); + @BeforeEach + public void createQuestionnaires() { + questionnairesContent(questionnaire, referenced1, referenced2); } @Test - void insertReference_loopOnReference() throws IllegalIterationException, IllegalFlowControlException { + void insertReference_loopOnReference() throws DeReferencingException { // IterationType iteration = new DynamicIterationType(); iteration.setId("loop1"); @@ -119,7 +72,7 @@ void insertReference_loopOnReference() throws IllegalIterationException, Illegal } @Test - void insertReference_referencedWithinLoop() throws IllegalIterationException, IllegalFlowControlException { + void insertReference_referencedWithinLoop() throws DeReferencingException { // Add second sequence in referenced SequenceType sequence12 = new SequenceType(); sequence12.setId("seq12"); @@ -141,7 +94,7 @@ void insertReference_referencedWithinLoop() throws IllegalIterationException, Il /** To make sure de-referencing doesn't affect loops that shouldn't be affected. */ @Test - void insertReference_referenceOutsideLoop() throws IllegalIterationException, IllegalFlowControlException { + void insertReference_referenceOutsideLoop() throws DeReferencingException { // questionnaire.setIterations(new Questionnaire.Iterations()); IterationType iteration1 = new DynamicIterationType(); @@ -164,7 +117,7 @@ void insertReference_referenceOutsideLoop() throws IllegalIterationException, Il } @Test - void insertReference_updateVariableScopes() throws IllegalIterationException, IllegalFlowControlException { + void insertReference_updateVariableScopes() throws DeReferencingException { // Add iteration on referenced 1 in referencing questionnaire IterationType iteration = new DynamicIterationType(); iteration.setId("loop1"); diff --git a/src/test/java/fr/insee/pogues/transforms/visualize/composition/UpdateFlowControlBoundsTest.java b/src/test/java/fr/insee/pogues/transforms/visualize/composition/UpdateFlowControlBoundsTest.java new file mode 100644 index 00000000..1b1282c1 --- /dev/null +++ b/src/test/java/fr/insee/pogues/transforms/visualize/composition/UpdateFlowControlBoundsTest.java @@ -0,0 +1,41 @@ +package fr.insee.pogues.transforms.visualize.composition; + +import fr.insee.pogues.exception.DeReferencingException; +import fr.insee.pogues.model.FlowControlType; +import fr.insee.pogues.model.Questionnaire; +import fr.insee.pogues.model.SequenceType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UpdateFlowControlBoundsTest { + + private final Questionnaire questionnaire = new Questionnaire(); + private final Questionnaire referenced1 = new Questionnaire(); + private final Questionnaire referenced2 = new Questionnaire(); + + @BeforeEach + public void createQuestionnaires() { + QuestionnaireCompositionTest.questionnairesContent(questionnaire, referenced1, referenced2); + } + + @Test + void endMemberIsReferencedQuestionnaire() throws DeReferencingException { + // + FlowControlType flowControlType = new FlowControlType(); + flowControlType.setId("filter1"); + flowControlType.setIfTrue("seq1-ref1"); + questionnaire.getFlowControl().add(flowControlType); + // + SequenceType sequence12 = new SequenceType(); + sequence12.setId("seq12"); + referenced1.getChild().add(sequence12); + // + UpdateFlowControlBounds updateFlowControlBounds = new UpdateFlowControlBounds(); + updateFlowControlBounds.apply(questionnaire, referenced1); + // + assertEquals("seq1-seq12", questionnaire.getFlowControl().get(0).getIfTrue()); + } + +} diff --git a/src/test/resources/persistence/VariablesService/l4i3m6qa.json b/src/test/resources/persistence/VariablesService/l4i3m6qa.json new file mode 100644 index 00000000..e6e4bbda --- /dev/null +++ b/src/test/resources/persistence/VariablesService/l4i3m6qa.json @@ -0,0 +1,374 @@ +{ + "owner": "FAKEPERMISSION", + "FlowControl": [], + "ComponentGroup": [ + { + "MemberReference": [ + "l4i3a6ii", + "l4i3it38", + "l4i3b1na", + "l4i3w0p1", + "idendquest" + ], + "Label": [ + "Components for page 1" + ], + "id": "l4i3je0b", + "Name": "PAGE_1" + } + ], + "agency": "fr.insee", + "genericName": "QUESTIONNAIRE", + "Label": [ + "Catalogue" + ], + "childQuestionnaireRef": [], + "Name": "CATALOGUE", + "Variables": { + "Variable": [ + { + "Formula": "41 + 1", + "Label": "CAT_VAR_COMP_1", + "id": "l50tn9cp", + "type": "CalculatedVariableType", + "Name": "CAT_VAR_COMP_1", + "Datatype": { + "Maximum": "100", + "Minimum": "0", + "typeName": "NUMERIC", + "Unit": "", + "type": "NumericDatatypeType", + "Decimals": "" + } + }, + { + "Label": "CAT_VAR_EXT_1", + "id": "l7hn33xv", + "type": "ExternalVariableType", + "Name": "CAT_VAR_EXT_1", + "Datatype": { + "Maximum": "100", + "Minimum": "0", + "typeName": "NUMERIC", + "Unit": "", + "type": "NumericDatatypeType", + "Decimals": "" + } + }, + { + "Label": "CAT_Q1 label", + "id": "l4i3ae42", + "type": "CollectedVariableType", + "Name": "CAT_Q1", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + }, + { + "Scope": "ldx9s262", + "Label": "1 - Netflix", + "id": "l4i3j8e1", + "type": "CollectedVariableType", + "Name": "CAT_Q21", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + }, + { + "Scope": "ldx9s262", + "Label": "2 - Canal+", + "id": "l4i3dbok", + "type": "CollectedVariableType", + "Name": "CAT_Q22", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + }, + { + "Scope": "ldx9s262", + "Label": "3 - Disnex+", + "id": "l4i3gycc", + "type": "CollectedVariableType", + "Name": "CAT_Q23", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + }, + { + "Scope": "ldx9s262", + "Label": "4 - Prime", + "id": "l4i3ikfy", + "type": "CollectedVariableType", + "Name": "CAT_Q24", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + } + ] + }, + "lastUpdatedDate": "Mon Mar 27 2023 16:19:04 GMT+0200 (heure d’été d’Europe centrale)", + "DataCollection": [ + { + "id": "esa-dc-2018", + "uri": "http://ddi:fr.insee:DataCollection.esa-dc-2018" + } + ], + "final": false, + "flowLogic": "FILTER", + "id": "l4i3m6qa", + "TargetMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ], + "CodeLists": { + "CodeList": [ + { + "Label": "ABONNEMENTS", + "id": "l4i3ehzf", + "Code": [ + { + "Parent": "", + "Label": "Netflix", + "Value": 1 + }, + { + "Parent": "", + "Label": "Canal+", + "Value": 2 + }, + { + "Parent": "", + "Label": "Disnex+", + "Value": 3 + }, + { + "Parent": "", + "Label": "Prime", + "Value": 4 + } + ], + "Name": "" + } + ] + }, + "Iterations": { + "Iteration": [ + { + "Maximum": "2", + "Minimum": "2", + "MemberReference": [ + "l4i3b1na" + ], + "id": "ldx9s262", + "Step": "1", + "type": "DynamicIterationType", + "Name": "BOUCLE_REF" + } + ] + }, + "formulasLanguage": "VTL", + "Child": [ + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "CAT_S1" + ], + "id": "l4i3a6ii", + "TargetMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "l4i3ae42", + "id": "l4i3k2ed", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"CAT_Q1\"" + ], + "id": "l4i3it38", + "TargetMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ], + "Declaration": [ + { + "declarationType": "HELP", + "Text": "\"With a twist\"", + "id": "l7fzaq2p", + "position": "AFTER_QUESTION_TEXT", + "DeclarationMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ] + } + ], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "CAT_Q1" + } + ], + "Name": "CAT_S1" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "CAT_S2" + ], + "id": "l4i3b1na", + "TargetMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "FlowControl": [], + "Label": [ + "\"CAT_Q2\"" + ], + "ResponseStructure": { + "Attribute": [], + "Mapping": [ + { + "MappingSource": "l4i3rxxm", + "MappingTarget": "1" + }, + { + "MappingSource": "l4i3lzwf", + "MappingTarget": "2" + }, + { + "MappingSource": "l4i3d9if", + "MappingTarget": "3" + }, + { + "MappingSource": "l4i3t95q", + "MappingTarget": "4" + } + ], + "Dimension": [ + { + "dimensionType": "PRIMARY", + "dynamic": "0", + "CodeListReference": "l4i3ehzf" + }, + { + "dimensionType": "MEASURE", + "dynamic": "0" + } + ] + }, + "type": "QuestionType", + "Name": "CAT_Q2", + "Response": [ + { + "CollectedVariableReference": "l4i3j8e1", + "id": "l4i3rxxm", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + }, + { + "CollectedVariableReference": "l4i3dbok", + "id": "l4i3lzwf", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + }, + { + "CollectedVariableReference": "l4i3gycc", + "id": "l4i3d9if", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + }, + { + "CollectedVariableReference": "l4i3ikfy", + "id": "l4i3t95q", + "Datatype": { + "typeName": "BOOLEAN", + "type": "BooleanDatatypeType" + } + } + ], + "Control": [], + "depth": 2, + "ClarificationQuestion": [], + "id": "l4i3w0p1", + "TargetMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ], + "Declaration": [], + "questionType": "MULTIPLE_CHOICE" + } + ], + "Name": "CAT_S2" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "QUESTIONNAIRE_END" + ], + "id": "idendquest", + "TargetMode": [ + "CAWI", + "PAPI", + "CATI", + "CAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [], + "Name": "QUESTIONNAIRE_END" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/transforms/PoguesJSONToPoguesJSONDeref/linked_loop/lgyr1y6x_host.json b/src/test/resources/transforms/PoguesJSONToPoguesJSONDeref/linked_loop/lgyr1y6x_host.json new file mode 100644 index 00000000..bcfc5d4a --- /dev/null +++ b/src/test/resources/transforms/PoguesJSONToPoguesJSONDeref/linked_loop/lgyr1y6x_host.json @@ -0,0 +1,319 @@ +{ + "owner": "FAKEPERMISSION", + "FlowControl": [], + "ComponentGroup": [ + { + "MemberReference": [ + "lgyrchd9", + "lgyr1smj", + "idendquest", + "lgyr3utb", + "lgyrfr5p", + "lgyrlyis", + "lgyrg0s1", + "lgyrgrx9" + ], + "Label": [ + "Components for page 1" + ], + "id": "lgyrhhm7", + "Name": "PAGE_1" + } + ], + "agency": "fr.insee", + "genericName": "QUESTIONNAIRE", + "Label": [ + "Issue Referenced Loop" + ], + "childQuestionnaireRef": [ + "lgyr3utb" + ], + "Name": "ISSUE_REF_LOOP", + "Variables": { + "Variable": [ + { + "Label": "QUESTION1 label", + "id": "lgyrkfo0", + "type": "CollectedVariableType", + "Name": "QUESTION1", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + }, + { + "Scope": "lgyriqvg", + "Label": "QUESTION2 label", + "id": "lgyrkt28", + "type": "CollectedVariableType", + "Name": "QUESTION2", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + }, + { + "Label": "QUSETION_LAST label", + "id": "lgys0cp9", + "type": "CollectedVariableType", + "Name": "QUSETION_LAST", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ] + }, + "lastUpdatedDate": "Thu Apr 27 2023 08:45:31 GMT+0200 (heure d’été d’Europe centrale)", + "DataCollection": [ + { + "id": "esa-dc-2018", + "uri": "http://ddi:fr.insee:DataCollection.esa-dc-2018", + "Name": "Enquête sectorielle annuelle 2018" + } + ], + "final": false, + "flowLogic": "FILTER", + "id": "lgyr1y6x", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "CodeLists": { + "CodeList": [] + }, + "Iterations": { + "Iteration": [ + { + "MemberReference": [ + "lgyrfr5p", + "lgyrfr5p" + ], + "id": "lgyrtkh8", + "type": "DynamicIterationType", + "Name": "LINKED_LOOP", + "IterableReference": "lgyriqvg" + } + ] + }, + "formulasLanguage": "VTL", + "Child": [ + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "\"Sequence 1\"" + ], + "id": "lgyrchd9", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "lgyrkfo0", + "id": "lgyrh1ne", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"Question 1\"" + ], + "id": "lgyr1smj", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "QUESTION1" + } + ], + "Name": "SEQUENCE1" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "EXTERNAL_ELEMENT", + "Label": [ + "Reference with loop" + ], + "id": "lgyr3utb", + "TargetMode": [ + "" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [], + "Name": "REF_WITH_LOOP" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "\"Sequence with linked loop\"" + ], + "id": "lgyrfr5p", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [ + { + "declarationType": "HELP", + "Text": "\"The loop on this sequence is based on a loop that comes from a referenced questionnaire.\"", + "id": "lgyrlt0g", + "position": "AFTER_QUESTION_TEXT", + "DeclarationMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ] + } + ], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "lgyrkt28", + "id": "lgys1e5c", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"Question 2\"" + ], + "id": "lgyrlyis", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "QUESTION2" + } + ], + "Name": "SEQ_LINKED_LOOP" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "\"End sequence\"" + ], + "id": "lgyrg0s1", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "lgys0cp9", + "id": "lgyrrrww", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"Last question\"" + ], + "id": "lgyrgrx9", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "QUSETION_LAST" + } + ], + "Name": "SEQ_END" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "QUESTIONNAIRE_END" + ], + "id": "idendquest", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [], + "Name": "QUESTIONNAIRE_END" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/transforms/PoguesJSONToPoguesJSONDeref/linked_loop/lgyr3utb_referenced.json b/src/test/resources/transforms/PoguesJSONToPoguesJSONDeref/linked_loop/lgyr3utb_referenced.json new file mode 100644 index 00000000..e83b74b4 --- /dev/null +++ b/src/test/resources/transforms/PoguesJSONToPoguesJSONDeref/linked_loop/lgyr3utb_referenced.json @@ -0,0 +1,289 @@ +{ + "owner": "FAKEPERMISSION", + "FlowControl": [], + "ComponentGroup": [ + { + "MemberReference": [ + "idendquest", + "lgyrd62v", + "lgyrnlm3", + "lgyr87p3", + "lgyrpxzv", + "lgyrpiip", + "lgyrgkez" + ], + "Label": [ + "Components for page 1" + ], + "id": "lgyrq5it", + "Name": "PAGE_1" + } + ], + "agency": "fr.insee", + "genericName": "QUESTIONNAIRE", + "Label": [ + "Reference with loop" + ], + "childQuestionnaireRef": [], + "Name": "REF_WITH_LOOP", + "Variables": { + "Variable": [ + { + "Label": "REF_Q1 label", + "id": "lgyr6g6b", + "type": "CollectedVariableType", + "Name": "REF_Q1", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + }, + { + "Scope": "lgyriqvg", + "Label": "REF_Q2 label", + "id": "lgyrbjh3", + "type": "CollectedVariableType", + "Name": "REF_Q2", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + }, + { + "Label": "REF_Q_LAST label", + "id": "lgyrdql4", + "type": "CollectedVariableType", + "Name": "REF_Q_LAST", + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ] + }, + "lastUpdatedDate": "Thu Apr 27 2023 08:41:41 GMT+0200 (heure d’été d’Europe centrale)", + "DataCollection": [ + { + "id": "fpe-dc-2019", + "uri": "http://ddi:fr.insee:DataCollection.fpe-dc-2019", + "Name": "Enquête auprès des salariés de l’État 2019" + } + ], + "final": false, + "flowLogic": "FILTER", + "id": "lgyr3utb", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "CodeLists": { + "CodeList": [] + }, + "Iterations": { + "Iteration": [ + { + "Maximum": "5", + "Minimum": "1", + "MemberReference": [ + "lgyr87p3", + "lgyr87p3" + ], + "Label": "\"Add answer\"", + "id": "lgyriqvg", + "Step": "1", + "type": "DynamicIterationType", + "Name": "LOOP" + } + ] + }, + "formulasLanguage": "VTL", + "Child": [ + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "\"Reference sequence 1\"" + ], + "id": "lgyrd62v", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "lgyr6g6b", + "id": "lgyrhnmj", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"Reference question 1\"" + ], + "id": "lgyrnlm3", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "REF_Q1" + } + ], + "Name": "REF_SEQ1" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "\"Sequence with loop\"" + ], + "id": "lgyr87p3", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "lgyrbjh3", + "id": "lgyrw53y", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"Reference question 2\"" + ], + "id": "lgyrpxzv", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "REF_Q2" + } + ], + "Name": "REF_SEQ_LOOP" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "\"Reference end sequence\"" + ], + "id": "lgyrpiip", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [ + { + "Response": [ + { + "CollectedVariableReference": "lgyrdql4", + "id": "lgyrvbwb", + "mandatory": false, + "Datatype": { + "Pattern": "", + "typeName": "TEXT", + "type": "TextDatatypeType", + "MaxLength": 249 + } + } + ], + "Control": [], + "depth": 2, + "FlowControl": [], + "Label": [ + "\"Reference last question\"" + ], + "id": "lgyrgkez", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "QuestionType", + "questionType": "SIMPLE", + "Name": "REF_Q_LAST" + } + ], + "Name": "REF_SEQ_END" + }, + { + "Control": [], + "depth": 1, + "FlowControl": [], + "genericName": "MODULE", + "Label": [ + "QUESTIONNAIRE_END" + ], + "id": "idendquest", + "TargetMode": [ + "CAPI", + "CATI", + "CAWI", + "PAPI" + ], + "Declaration": [], + "type": "SequenceType", + "Child": [], + "Name": "QUESTIONNAIRE_END" + } + ] +} \ No newline at end of file