Skip to content

Commit

Permalink
feat: roundabout
Browse files Browse the repository at this point in the history
  • Loading branch information
nsenave committed Jun 26, 2024
1 parent a21d384 commit b2a1186
Show file tree
Hide file tree
Showing 16 changed files with 1,797 additions and 11 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.22.8'
version = '3.23.0-SNAPSHOT'
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package fr.insee.eno.core.exceptions.business;

public class LunaticVariableConflictException extends RuntimeException {

public LunaticVariableConflictException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import fr.insee.eno.core.model.navigation.Loop;
import fr.insee.eno.core.model.question.MultipleResponseQuestion;
import fr.insee.eno.core.model.question.SingleResponseQuestion;
import fr.insee.eno.core.model.sequence.RoundaboutSequence;
import fr.insee.eno.core.model.sequence.Sequence;
import fr.insee.eno.core.model.sequence.Subsequence;
import fr.insee.eno.core.model.variable.Variable;
Expand Down Expand Up @@ -72,6 +73,14 @@ public class EnoQuestionnaire extends EnoIdentifiableObject {
@Lunatic("getComponents()")
private final List<Subsequence> subsequences = new ArrayList<>();

/** Roundabouts are described as a special type of sequence in DDI.
* These are resolved in Lunatic through a dedicated processing step. */
@DDI("getResourcePackageArray(0).getControlConstructSchemeArray(0).getControlConstructList()" +
".?[#this instanceof T(datacollection33.SequenceType) " +
"and not #this.getTypeOfSequenceList().isEmpty()]" +
".?[#this.getTypeOfSequenceArray(0).getStringValue() == 'roundabout']")
private final List<RoundaboutSequence> roundaboutSequences = new ArrayList<>();

/** Loops defined in the questionnaire.
* In DDI, a loop is defined at the questionnaire level.
* In Lunatic, a loop is a component containing components within its scope.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static Criticality convertDDICriticality(String ddiTypeOfComputationItem)
return switch (ddiTypeOfComputationItem) {
case "informational", "line-informational" -> Criticality.INFO;
case "warning", "line-warning" -> Criticality.WARN;
case "stumblingblock", "line-stumblingblock" -> Criticality.ERROR;
case "stumblingblock", "line-stumblingblock", "roundabout-locked" -> Criticality.ERROR;
default -> throw new MappingException(String.format(
"Unknown DDI criticality '%s'", ddiTypeOfComputationItem));
};
Expand Down Expand Up @@ -101,7 +101,7 @@ public static Context mapContextFromDDI(ComputationItemType computationItemType)
private Context context;

/** Label typed in Pogues, unused in Lunatic. */
@DDI("getDescription().getContentArray(0).getStringValue()") // NOTE: getConstructNameArray(0).getStringArray(0).getStringValue() has the same information
@DDI("getDescription()?.getContentArray(0)?.getStringValue()") // NOTE: getConstructNameArray(0).getStringArray(0).getStringValue() has the same information
private String label;

/** Expression that determines if the control is triggered or not. */
Expand All @@ -110,8 +110,10 @@ public static Context mapContextFromDDI(ComputationItemType computationItemType)
private CalculatedExpression expression;

/** Message displayed if the control is triggered. */
@DDI("#index.get(#this.getInterviewerInstructionReferenceArray(0).getIDArray(0).getStringValue())" +
".getInstructionTextArray(0)")
@DDI("!#this.getInterviewerInstructionReferenceList().isEmpty() ? " +
"#index.get(#this.getInterviewerInstructionReferenceArray(0).getIDArray(0).getStringValue())" +
".getInstructionTextArray(0) : " +
"null")
@Lunatic("setErrorMessage(#param)")
private DynamicLabel message;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public abstract class Loop extends EnoIdentifiableObject {
@DDI("T(fr.insee.eno.core.model.navigation.Loop).mapLoopItemReferences(#this, #index)")
private final List<ItemReference> loopItems = new ArrayList<>();

/** The occurrences of a loop can be filtered.
* This attribute holds the identifier of the occurrence filter object.
* null if there is no occurrence filter in the loop. */
@DDI("getControlConstructReference().getTypeOfObject().toString() == 'IfThenElse' ? " +
"getControlConstructReference().getIDArray(0).getStringValue() : null")
private String occurrenceFilterId;

/** References of sequences or subsequences that are in the scope of the loop.
* Note: in Pogues a loop can only be defined on sequence or subsequences.
* (In other formats, nothing makes it formally impossible to have loops defined directly on questions.)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fr.insee.eno.core.model.sequence;

import datacollection33.SequenceType;
import fr.insee.eno.core.annotations.Contexts.Context;
import fr.insee.eno.core.annotations.DDI;
import fr.insee.eno.core.parameter.Format;
import lombok.Getter;
import lombok.Setter;

/**
* Special kind of sequence that describes a "roundabout".
* A roundabout is like a loop with refinements.
* In DDI, the roundabout is described as a DDI loop, with a DDI sequence that encapsulates it.
* In Lunatic, roundabout components are resolved in a dedicated processing.
*/
@Getter
@Setter
@Context(format = Format.DDI, type = SequenceType.class)
public class RoundaboutSequence extends AbstractSequence {

/** DDI reference of the loop.
* Note: mapped as the id of the first control construct reference. */
@DDI("getControlConstructReferenceArray(0).getIDArray(0).getStringValue()")
private String loopReference;

/** Boolean that describes if the completed occurrences should be locked or not.
* In DDI, this is modeled by the presence or not of a ComputationItem among the control construct references. */
@DDI("!getControlConstructReferenceList().?[#this.getTypeOfObject().toString() == 'ComputationItem'].isEmpty()")
private Boolean locked;

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public void apply(EnoQuestionnaire enoQuestionnaire) {
* Return true if the given instruction matches the selected modes from parameters.
*/
private boolean hasNoSelectedMode(DeclarationInterface declaration) {
// If no mode is declared, the declaration is considered valid for all modes
if (declaration.getModes().isEmpty())
return false;
return declaration.getModes().stream().noneMatch(selectedModes::contains);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public void applyProcessing(Questionnaire lunaticQuestionnaire, EnoQuestionnaire
.then(new LunaticAddGeneratingDate())
.then(new LunaticSortComponents(enoQuestionnaire))
.then(new LunaticLoopResolution(enoQuestionnaire))
.then(new LunaticRoundaboutLoops(enoQuestionnaire))
.then(new LunaticTableProcessing(enoQuestionnaire))
.then(new LunaticInsertUniqueChoiceDetails(enoQuestionnaire))
.then(new LunaticDropdownLabels()) // this step should be temporary
Expand Down
Loading

0 comments on commit b2a1186

Please sign in to comment.