-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(dynamic table): resizing for size expression (#1181)
- Loading branch information
Showing
5 changed files
with
218 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
...a/fr/insee/eno/core/processing/out/steps/lunatic/resizing/LunaticRosterResizingLogic.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package fr.insee.eno.core.processing.out.steps.lunatic.resizing; | ||
|
||
import fr.insee.eno.core.exceptions.technical.MappingException; | ||
import fr.insee.eno.core.model.EnoQuestionnaire; | ||
import fr.insee.eno.core.model.calculated.BindingReference; | ||
import fr.insee.eno.core.model.question.DynamicTableQuestion; | ||
import fr.insee.lunatic.model.flat.*; | ||
import fr.insee.lunatic.model.flat.variable.VariableType; | ||
import fr.insee.lunatic.model.flat.variable.VariableTypeEnum; | ||
|
||
import java.util.LinkedHashSet; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import static fr.insee.eno.core.processing.out.steps.lunatic.resizing.LunaticLoopResizingLogic.insertIterationEntry; | ||
|
||
public class LunaticRosterResizingLogic { | ||
|
||
private final Questionnaire lunaticQuestionnaire; | ||
private final EnoQuestionnaire enoQuestionnaire; | ||
|
||
public LunaticRosterResizingLogic(Questionnaire lunaticQuestionnaire, EnoQuestionnaire enoQuestionnaire) { | ||
this.lunaticQuestionnaire = lunaticQuestionnaire; | ||
this.enoQuestionnaire = enoQuestionnaire; | ||
} | ||
|
||
/** | ||
* Insert resizing entries for the given roster component. | ||
* @param lunaticRoster Lunatic RosterForLoop (dynamic table) object. | ||
* @param lunaticResizing Lunatic resizing block object. | ||
*/ | ||
public void buildResizingEntries(RosterForLoop lunaticRoster, ResizingType lunaticResizing) { | ||
|
||
// Corresponding Eno loop object | ||
DynamicTableQuestion enoDynamicTable = getDynamicTable(enoQuestionnaire, lunaticRoster.getId()); | ||
|
||
// If the dynamic table size is not defined by a VTL expression, nothing to do here | ||
if (enoDynamicTable.getSizeExpression() == null) | ||
return; | ||
|
||
// Variable names that are the keys of the resizing (using a set to make sure there is no duplicates) | ||
Set<String> resizingVariableNames = findResizingVariablesForRoster(enoDynamicTable); | ||
|
||
// Expression that resizes the concerned variables | ||
String sizeExpression = enoDynamicTable.getSizeExpression().getValue(); | ||
|
||
// Concerned variables to be resized: responses of the roster component | ||
List<String> resizedVariableNames = lunaticRoster.getComponents().stream() | ||
.map(BodyCell::getResponse).map(ResponseType::getName).toList(); | ||
|
||
// Insert resizing entries (the logic is the same as for loops) | ||
resizingVariableNames.forEach(variableName -> insertIterationEntry( | ||
lunaticResizing, variableName, sizeExpression, resizedVariableNames)); | ||
} | ||
|
||
private DynamicTableQuestion getDynamicTable(EnoQuestionnaire enoQuestionnaire, String id) { | ||
Optional<DynamicTableQuestion> searched = enoQuestionnaire.getMultipleResponseQuestions().stream() | ||
.filter(DynamicTableQuestion.class::isInstance).map(DynamicTableQuestion.class::cast) | ||
.filter(dynamicTableQuestion -> id.equals(dynamicTableQuestion.getId())) | ||
.findAny(); | ||
if (searched.isEmpty()) | ||
throw new MappingException("Cannot find dynamic table question of id " + id); | ||
return searched.get(); | ||
} | ||
|
||
private Set<String> findResizingVariablesForRoster(DynamicTableQuestion enoDynamicTable) { | ||
List<String> sizeDependencies = enoDynamicTable.getSizeExpression().getBindingReferences().stream() | ||
.map(BindingReference::getVariableName) | ||
.toList(); | ||
// Note: we could simply return this dependencies list, | ||
// but we use the Lunatic questionnaire to filter non-collected variables | ||
return lunaticQuestionnaire.getVariables().stream() | ||
.filter(variable -> VariableTypeEnum.COLLECTED.equals(variable.getVariableType())) | ||
.map(VariableType::getName) | ||
.filter(sizeDependencies::contains) | ||
.collect(Collectors.toCollection(LinkedHashSet::new)); | ||
// NB: using a linked hash set to preserve the same order in different generations | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
.../insee/eno/core/processing/out/steps/lunatic/resizing/LunaticRosterResizingLogicTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package fr.insee.eno.core.processing.out.steps.lunatic.resizing; | ||
|
||
import fr.insee.eno.core.DDIToEno; | ||
import fr.insee.eno.core.exceptions.business.DDIParsingException; | ||
import fr.insee.eno.core.mappers.LunaticMapper; | ||
import fr.insee.eno.core.model.EnoQuestionnaire; | ||
import fr.insee.eno.core.model.calculated.BindingReference; | ||
import fr.insee.eno.core.model.calculated.CalculatedExpression; | ||
import fr.insee.eno.core.model.question.DynamicTableQuestion; | ||
import fr.insee.eno.core.parameter.EnoParameters; | ||
import fr.insee.eno.core.processing.out.steps.lunatic.LunaticLoopResolution; | ||
import fr.insee.eno.core.processing.out.steps.lunatic.LunaticSortComponents; | ||
import fr.insee.eno.core.processing.out.steps.lunatic.table.LunaticTableProcessing; | ||
import fr.insee.lunatic.model.flat.*; | ||
import fr.insee.lunatic.model.flat.variable.CollectedVariableType; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.math.BigInteger; | ||
import java.util.List; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
class LunaticRosterResizingLogicTest { | ||
|
||
@Test | ||
@DisplayName("Dynamic table with size expression: should have a resizing entry.") | ||
void unitTest1() { | ||
// Given | ||
EnoQuestionnaire enoQuestionnaire = new EnoQuestionnaire(); | ||
DynamicTableQuestion enoDynamicTable = new DynamicTableQuestion(); | ||
enoDynamicTable.setId("dynamic-table-id"); | ||
CalculatedExpression sizeExpression = new CalculatedExpression(); | ||
sizeExpression.setValue("<foo expression>"); | ||
sizeExpression.getBindingReferences().add(new BindingReference("foo-ref","FOO_COLLECTED_VARIABLE")); | ||
enoDynamicTable.setSizeExpression(sizeExpression); | ||
enoQuestionnaire.getMultipleResponseQuestions().add(enoDynamicTable); | ||
|
||
Questionnaire lunaticQuestionnaire = new Questionnaire(); | ||
RosterForLoop lunaticRoster = new RosterForLoop(); | ||
lunaticRoster.setId("dynamic-table-id"); | ||
BodyCell column1Component = new BodyCell(); | ||
column1Component.setResponse(new ResponseType()); | ||
column1Component.getResponse().setName("TABLE_VAR1"); | ||
BodyCell column2Component = new BodyCell(); | ||
column2Component.setResponse(new ResponseType()); | ||
column2Component.getResponse().setName("TABLE_VAR2"); | ||
lunaticRoster.getComponents().add(column1Component); | ||
lunaticRoster.getComponents().add(column2Component); | ||
lunaticQuestionnaire.getComponents().add(lunaticRoster); | ||
|
||
CollectedVariableType lunaticVariable = new CollectedVariableType(); | ||
lunaticVariable.setName("FOO_COLLECTED_VARIABLE"); | ||
lunaticQuestionnaire.getVariables().add(lunaticVariable); | ||
|
||
// When | ||
ResizingType lunaticResizing = new ResizingType(); | ||
new LunaticRosterResizingLogic(lunaticQuestionnaire, enoQuestionnaire) | ||
.buildResizingEntries(lunaticRoster, lunaticResizing); | ||
|
||
// Then | ||
assertEquals(1, lunaticResizing.countResizingEntries()); | ||
assertEquals("<foo expression>", | ||
lunaticResizing.getResizingEntry("FOO_COLLECTED_VARIABLE").getSize()); | ||
assertEquals(List.of("TABLE_VAR1", "TABLE_VAR2"), | ||
lunaticResizing.getResizingEntry("FOO_COLLECTED_VARIABLE").getVariables()); | ||
} | ||
|
||
@Test | ||
@DisplayName("Dynamic table with size expression: should have no resizing entries.") | ||
void unitTest2() { | ||
// Given | ||
EnoQuestionnaire enoQuestionnaire = new EnoQuestionnaire(); | ||
DynamicTableQuestion enoDynamicTable = new DynamicTableQuestion(); | ||
enoDynamicTable.setId("dynamic-table-id"); | ||
enoDynamicTable.setMinLines(BigInteger.ONE); | ||
enoDynamicTable.setMaxLines(BigInteger.TEN); | ||
enoQuestionnaire.getMultipleResponseQuestions().add(enoDynamicTable); | ||
|
||
Questionnaire lunaticQuestionnaire = new Questionnaire(); | ||
RosterForLoop lunaticRoster = new RosterForLoop(); | ||
lunaticRoster.setId("dynamic-table-id"); | ||
BodyCell column1Component = new BodyCell(); | ||
column1Component.setResponse(new ResponseType()); | ||
column1Component.getResponse().setName("TABLE_VAR1"); | ||
BodyCell column2Component = new BodyCell(); | ||
column2Component.setResponse(new ResponseType()); | ||
column2Component.getResponse().setName("TABLE_VAR2"); | ||
lunaticRoster.getComponents().add(column1Component); | ||
lunaticRoster.getComponents().add(column2Component); | ||
lunaticQuestionnaire.getComponents().add(lunaticRoster); | ||
|
||
// When | ||
ResizingType lunaticResizing = new ResizingType(); | ||
new LunaticRosterResizingLogic(lunaticQuestionnaire, enoQuestionnaire) | ||
.buildResizingEntries(lunaticRoster, lunaticResizing); | ||
|
||
// Then | ||
assertEquals(0, lunaticResizing.countResizingEntries()); | ||
} | ||
|
||
@Test | ||
void integrationTest() throws DDIParsingException { | ||
// Given | ||
EnoQuestionnaire enoQuestionnaire = DDIToEno.transform( | ||
LunaticAddResizingTest.class.getClassLoader().getResourceAsStream( | ||
"integration/ddi/ddi-dynamic-table-size.xml"), | ||
EnoParameters.of(EnoParameters.Context.BUSINESS, EnoParameters.ModeParameter.CAWI)); | ||
Questionnaire lunaticQuestionnaire = new Questionnaire(); | ||
LunaticMapper lunaticMapper = new LunaticMapper(); | ||
lunaticMapper.mapQuestionnaire(enoQuestionnaire, lunaticQuestionnaire); | ||
new LunaticSortComponents(enoQuestionnaire).apply(lunaticQuestionnaire); | ||
new LunaticLoopResolution(enoQuestionnaire).apply(lunaticQuestionnaire); | ||
new LunaticTableProcessing(enoQuestionnaire).apply(lunaticQuestionnaire); | ||
|
||
// When | ||
new LunaticAddResizing(enoQuestionnaire).apply(lunaticQuestionnaire); | ||
|
||
// Then | ||
ResizingType lunaticResizing = lunaticQuestionnaire.getResizing(); | ||
assertEquals(1, lunaticResizing.countResizingEntries()); | ||
assertEquals("cast(HOW_MANY, integer)", | ||
lunaticResizing.getResizingEntry("HOW_MANY").getSize()); | ||
assertEquals(List.of("DYNAMIC_TABLE_VTL_SIZE1"), | ||
lunaticResizing.getResizingEntry("HOW_MANY").getVariables()); | ||
} | ||
|
||
} |