Skip to content

Commit

Permalink
fix: missing response for pairwise links component (#842)
Browse files Browse the repository at this point in the history
  • Loading branch information
nsenave authored Jan 4, 2024
1 parent 9dbc2e6 commit 3a65290
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 57 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tasks.withType(JavaCompile).configureEach {

allprojects {
group = 'fr.insee.eno'
version = '3.13.6'
version = '3.13.7'
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
package fr.insee.eno.core.processing.out.steps.lunatic;

import fr.insee.eno.core.exceptions.business.LunaticLoopException;
import fr.insee.eno.core.exceptions.technical.MappingException;
import fr.insee.eno.core.model.lunatic.MissingBlock;
import fr.insee.eno.core.model.question.Question;
import fr.insee.eno.core.processing.ProcessingStep;
import fr.insee.eno.core.reference.EnoCatalog;
import fr.insee.lunatic.model.flat.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@AllArgsConstructor
@Slf4j
public class LunaticAddMissingVariables implements ProcessingStep<Questionnaire> {

private EnoCatalog enoCatalog;
private boolean isMissingVariables;
private final EnoCatalog enoCatalog;
private final boolean isMissingVariables;

private static final String MISSING_RESPONSE_SUFFIX = "_MISSING";

public LunaticAddMissingVariables(EnoCatalog enoCatalog, boolean isMissingVariables) {
this.enoCatalog = enoCatalog;
this.isMissingVariables = isMissingVariables;
}

/**
* process missing responses on lunatic questionnaire
*
* @param lunaticQuestionnaire Out object to be processed.
*/
public void apply(Questionnaire lunaticQuestionnaire) {
Expand Down Expand Up @@ -51,7 +57,7 @@ public void apply(Questionnaire lunaticQuestionnaire) {
allMissingBlocks.addAll(createReversedMissingBlocks(missingBlock));
});

if(!allMissingBlocks.isEmpty()) {
if (!allMissingBlocks.isEmpty()) {
MissingType missingType = new MissingType();
missingType.getAny().addAll(allMissingBlocks);

Expand All @@ -62,7 +68,8 @@ public void apply(Questionnaire lunaticQuestionnaire) {

/**
* create the reversed missing blocks from a missing block
* @param missingBlock missing block from which we ne to create reversed missing blocks
*
* @param missingBlock missing block from which we need to create reversed missing blocks
* @return list of reversed missing blocks
*/
private List<MissingBlock> createReversedMissingBlocks(MissingBlock missingBlock) {
Expand All @@ -73,6 +80,7 @@ private List<MissingBlock> createReversedMissingBlocks(MissingBlock missingBlock

/**
* create missing blocks from components
*
* @param components list of components to process
* @return list of missing blocks
*/
Expand All @@ -97,7 +105,7 @@ private List<MissingBlock> createMissingBlocks(List<ComponentType> components) {
.flatMap(Collection::stream)
.toList());

// generate blocks for subcomponents on pairwiselinks
// generate blocks for subcomponents on pairwise links
missingBlocks.addAll(components.stream()
.filter(componentType -> componentType.getComponentType().equals(ComponentTypeEnum.PAIRWISE_LINKS))
.map(PairwiseLinks.class::cast)
Expand All @@ -110,34 +118,31 @@ private List<MissingBlock> createMissingBlocks(List<ComponentType> components) {

/**
* Extract the names of a missing block from a component
*
* @param component component which we extract missing block names
* @return list of names
*/
private List<String> getMissingBlockNames(ComponentType component) {
List<String> names;
switch(component.getComponentType()) {
case CHECKBOX_GROUP ->
names = ((CheckboxGroup)component).getResponses().stream()
.map(ResponsesCheckboxGroup::getResponse)
.map(ResponseType::getName)
.toList();
case ROSTER_FOR_LOOP ->
names = ((RosterForLoop)component).getComponents().stream()
.filter(subcomponent -> subcomponent.getResponse() != null)
.map(subcomponent -> subcomponent.getResponse().getName())
.toList();
case LOOP ->
names = filterComponentsToProcess(((Loop)component).getComponents()).stream()
.map(this::getMissingBlockNames)
.flatMap(Collection::stream)
.toList();
case TABLE ->
names = ((Table)component).getBodyLines().stream()
.map(BodyLine::getBodyCells)
.flatMap(Collection::stream)
.filter(subcomponent -> subcomponent.getResponse() != null)
.map(subcomponent -> subcomponent.getResponse().getName())
.toList();
switch (component.getComponentType()) {
case CHECKBOX_GROUP -> names = ((CheckboxGroup) component).getResponses().stream()
.map(ResponsesCheckboxGroup::getResponse)
.map(ResponseType::getName)
.toList();
case ROSTER_FOR_LOOP -> names = ((RosterForLoop) component).getComponents().stream()
.filter(subcomponent -> subcomponent.getResponse() != null)
.map(subcomponent -> subcomponent.getResponse().getName())
.toList();
case LOOP -> names = filterComponentsToProcess(((Loop) component).getComponents()).stream()
.map(this::getMissingBlockNames)
.flatMap(Collection::stream)
.toList();
case TABLE -> names = ((Table) component).getBodyLines().stream()
.map(BodyLine::getBodyCells)
.flatMap(Collection::stream)
.filter(subcomponent -> subcomponent.getResponse() != null)
.map(subcomponent -> subcomponent.getResponse().getName())
.toList();
default -> {
ComponentSimpleResponseType simpleResponseComponent = (ComponentSimpleResponseType) component;
names = List.of(simpleResponseComponent.getResponse().getName());
Expand All @@ -148,22 +153,23 @@ private List<String> getMissingBlockNames(ComponentType component) {

/**
* set missing response for a component
*
* @param component set missing response for this component
*/
private void setComponentMissingResponse(ComponentType component) {
String missingResponseName = null;

switch(component.getComponentType()) {
switch (component.getComponentType()) {
case LOOP -> {
Loop loop = (Loop) component;
// when linked loop, missing responses are generated on the loop components
if(isLinkedLoop(loop)) {
if (isLinkedLoop(loop)) {
filterComponentsToProcess(loop.getComponents()).forEach(this::setComponentMissingResponse);
return;
}

//on main loop, missing response is generated on the loop component
// /!\ we assume the first question component found is a simple question (not roster, table, checkboxgroup, ...)
// /!\ we assume the first question component found is a simple question (not roster, table, checkbox group, ...)
missingResponseName = loop.getComponents().stream()
.filter(ComponentSimpleResponseType.class::isInstance)
.map(ComponentSimpleResponseType.class::cast)
Expand All @@ -175,12 +181,26 @@ private void setComponentMissingResponse(ComponentType component) {
}

// missing responses are handled on the components of pairwise
case PAIRWISE_LINKS -> ((PairwiseLinks) component).getComponents().forEach(this::setComponentMissingResponse);
case PAIRWISE_LINKS -> {
List<ComponentType> pairwiseComponents = ((PairwiseLinks) component).getComponents();
if (pairwiseComponents.size() != 1)
throw new MappingException(String.format(
"Pairwise component '%s' has %s inner components (should be exactly 1).",
component.getId(), pairwiseComponents.size()));
ComponentType pairwiseInnerComponent = pairwiseComponents.get(0);
ResponseType missingResponse = new ResponseType();
missingResponse.setName(
((ComponentSimpleResponseType) pairwiseInnerComponent).getResponse() + MISSING_RESPONSE_SUFFIX);
pairwiseInnerComponent.setMissingResponse(missingResponse);
}

default -> missingResponseName = enoCatalog.getQuestion(component.getId()).getName();
default -> {
Question question = enoCatalog.getQuestion(component.getId());
missingResponseName = question.getName();
}
}

if(missingResponseName != null) {
if (missingResponseName != null) {
missingResponseName += MISSING_RESPONSE_SUFFIX;
ResponseType missingResponse = new ResponseType();
missingResponse.setName(missingResponseName);
Expand All @@ -190,6 +210,7 @@ private void setComponentMissingResponse(ComponentType component) {

/**
* filter components to process (only questions/loops)
*
* @param components components needing filtering
* @return filtered components
*/
Expand All @@ -203,6 +224,7 @@ private List<ComponentType> filterComponentsToProcess(List<ComponentType> compon

/**
* Check if loop is a main or linked loop
*
* @param loop loop to check
* @return true if linked loop, false otherwise
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import fr.insee.eno.core.model.lunatic.MissingBlock;
import fr.insee.eno.core.model.question.SingleResponseQuestion;
import fr.insee.eno.core.model.question.TextQuestion;
import fr.insee.eno.core.processing.out.steps.lunatic.LunaticAddMissingVariables;
import fr.insee.eno.core.reference.EnoCatalog;
import fr.insee.lunatic.model.flat.*;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -65,12 +64,12 @@ void whenSimpleQuestionsGenerateCorrectMissingResponse() {
lunaticQuestionnaire = new Questionnaire();
List<ComponentType> components = lunaticQuestionnaire.getComponents();
components.addAll(List.of(i, ta, n, d, cb, r, co, dd));
List<ComponentSimpleResponseType> responseTypes = components.stream().map(ComponentSimpleResponseType.class::cast).toList();

processing.apply(lunaticQuestionnaire);

for(int cpt=0; cpt<components.size(); cpt++) {
ComponentType component = components.get(cpt);
assertEquals(component.getMissingResponse().getName(), enoCatalog.getQuestion(component.getId()).getName()+"_MISSING");
for (ComponentType component : components) {
assertEquals(component.getMissingResponse().getName(),
enoCatalog.getQuestion(component.getId()).getName() + "_MISSING");
}
}

Expand Down Expand Up @@ -251,28 +250,23 @@ void whenPairwiseGenerateMissingBlocksFromSubComponents() {
lunaticQuestionnaire = new Questionnaire();
List<ComponentType> questionnaireComponents = lunaticQuestionnaire.getComponents();


List<ComponentType> pairwiseComponents = new ArrayList<>(List.of(i, ta));
List<ComponentType> pairwiseComponents = new ArrayList<>(List.of(dd));
PairwiseLinks pairwiseLinks = buildPairWiseLinks("jghdkpdf", pairwiseComponents);
questionnaireComponents.add(pairwiseLinks);
processing.apply(lunaticQuestionnaire);

List<ComponentSimpleResponseType> responseTypes = pairwiseComponents.stream().map(ComponentSimpleResponseType.class::cast).toList();
ComponentType pairwiseInnerComponent = pairwiseComponents.get(0);
List<MissingBlock> missingBlocks = lunaticQuestionnaire.getMissingBlock().getAny().stream()
.map(MissingBlock.class::cast).toList();

for(int cpt=0; cpt<pairwiseComponents.size(); cpt++) {
ComponentType component = pairwiseComponents.get(cpt);
ComponentSimpleResponseType simpleResponseType = responseTypes.get(cpt);
assertTrue(missingBlocks.stream()
.anyMatch(missingBlock -> missingBlock.getMissingName().equals(component.getMissingResponse().getName())
&& missingBlock.getNames().size() == 1
&& missingBlock.getNames().contains(simpleResponseType.getResponse().getName())));
assertTrue(missingBlocks.stream()
.anyMatch(missingBlock -> missingBlock.getMissingName().equals(simpleResponseType.getResponse().getName())
&& missingBlock.getNames().size() == 1
&& missingBlock.getNames().contains(component.getMissingResponse().getName())));
}
assertTrue(missingBlocks.stream().anyMatch(missingBlock ->
missingBlock.getMissingName().equals(pairwiseInnerComponent.getMissingResponse().getName())
&& missingBlock.getNames().size() == 1
&& missingBlock.getNames().contains(((ComponentSimpleResponseType) pairwiseInnerComponent).getResponse().getName())));
assertTrue(missingBlocks.stream().anyMatch(missingBlock ->
missingBlock.getMissingName().equals(((ComponentSimpleResponseType) pairwiseInnerComponent).getResponse().getName())
&& missingBlock.getNames().size() == 1
&& missingBlock.getNames().contains(pairwiseInnerComponent.getMissingResponse().getName())));
}

@Test
Expand Down

0 comments on commit 3a65290

Please sign in to comment.