Skip to content

Commit

Permalink
feat: roundabout component (#255)
Browse files Browse the repository at this point in the history
* feat: roundabout component

* refactor: reorder some component properties

* chore: release version 3.12.0
  • Loading branch information
nsenave authored Jul 4, 2024
1 parent 3ad4417 commit 028afc8
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 12 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<artifactId>lunatic-model</artifactId>
<packaging>jar</packaging>

<version>3.11.0</version>
<version>3.12.0</version>
<name>Lunatic Model</name>
<description>Classes and converters for the Lunatic model</description>
<url>https://inseefr.github.io/Lunatic-Model/</url>
Expand Down
18 changes: 12 additions & 6 deletions src/main/java/fr/insee/lunatic/model/flat/ComponentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,33 @@
"mandatory",
"page",
"goToPage",
"maxLength",
"label",
"orientation",
"positioning",
"maxLength",
"min",
"max",
"decimals",
"label",
"dateFormat",
"format",
"unit",
"declarations",
"conditionFilter",
"controls",
"hierarchy",
"missingResponse",
"storeName",
"bindingDependencies",
"components",
"lines",
"iterations",
"locked",
"progressVariable",
"header",
"body",
"options",
"dateFormat",
"format",
"unit",
"item",
"items",
"components",
"response",
"responses",
"suggesters",
Expand All @@ -63,6 +68,7 @@
@JsonSubTypes.Type(value = Question.class, name = "Question"),
@JsonSubTypes.Type(value = RosterForLoop.class, name = "RosterForLoop"),
@JsonSubTypes.Type(value = Loop.class, name = "Loop"),
@JsonSubTypes.Type(value = Roundabout.class, name = "Roundabout"),
@JsonSubTypes.Type(value = Table.class, name = "Table"),
@JsonSubTypes.Type(value = Input.class, name = "Input"),
@JsonSubTypes.Type(value = PairwiseLinks.class, name = "PairwiseLinks"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum ComponentTypeEnum {
QUESTION("Question"),
ROSTER_FOR_LOOP("RosterForLoop"),
LOOP("Loop"),
ROUNDABOUT("Roundabout"),
TABLE("Table"),
INPUT("Input"),
PAIRWISE_LINKS("PairwiseLinks"),
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/fr/insee/lunatic/model/flat/Input.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@

import java.math.BigInteger;

/**
* Input response component to collect a single line of text.
*/
@Getter
@Setter
public class Input
extends ComponentType
implements ComponentSimpleResponseType
{
public class Input extends ComponentType implements ComponentSimpleResponseType {

public Input() {
super();
this.componentType = ComponentTypeEnum.INPUT;
}

/** Regular expression that the input's content must match. (Unused yet in Lunatic.) */
protected String format;

/** {@link ResponseType} */
@JsonProperty(required = true)
protected ResponseType response;

/** Maximal length of the text that can be inputted. */
protected BigInteger maxLength;

}
2 changes: 1 addition & 1 deletion src/main/java/fr/insee/lunatic/model/flat/Question.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public static boolean isQuestionComponent(ComponentType component) {
case CHECKBOX_BOOLEAN, INPUT, TEXTAREA, INPUT_NUMBER, DATEPICKER, DURATION,
CHECKBOX_ONE, RADIO, DROPDOWN, SUGGESTER, TEXT,
CHECKBOX_GROUP, TABLE, ROSTER_FOR_LOOP, PAIRWISE_LINKS -> true;
case QUESTIONNAIRE, SEQUENCE, SUBSEQUENCE, QUESTION, ACCORDION, LOOP -> false;
case QUESTIONNAIRE, SEQUENCE, SUBSEQUENCE, QUESTION, ACCORDION, LOOP, ROUNDABOUT -> false;
};
}

Expand Down
57 changes: 57 additions & 0 deletions src/main/java/fr/insee/lunatic/model/flat/Roundabout.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package fr.insee.lunatic.model.flat;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;

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

/**
* Roundabout component. Its behavior is similar to a loop, but with refinements.
* The core idea is to display a menu (the "roundabout") for the iterations.
*/
@Getter
@Setter
public class Roundabout extends ComponentType implements ComponentNestingType {

/**
* Object that holds the configuration of roundabout's items.
*/
@Getter
@Setter
public static class Item {

/** Label of the items collected in the roundabout. */
@JsonProperty(required = true)
private LabelType label;

/** Optional description of the items collected in the roundabout. */
private LabelType description;

/** Expression that determines if a roundabout's item has to receive answers or not. */
private LabelType disabled;
}

public Roundabout() {
super();
this.componentType = ComponentTypeEnum.ROUNDABOUT;
this.components = new ArrayList<>();
}

/** Expression that defines the number of items in the roundabout. */
private LabelType iterations;

/** Boolean option to lock or not an item answers when it gets the 'completed' status. */
private Boolean locked;

/** Name of the variable that stores the current progress in each roundabout's item. */
private String progressVariable;

/** {@link Item} */
private Item item;

/** Roundabout components (sequences and response components). */
private List<ComponentType> components;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fr.insee.lunatic.conversion;

import fr.insee.lunatic.exception.SerializationException;
import fr.insee.lunatic.model.flat.*;
import fr.insee.lunatic.utils.TestUtils;
import org.json.JSONException;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import static fr.insee.lunatic.utils.TestUtils.createLabel;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;

class RoundaboutSerializationTest {

@Test
void serializeRoundabout() throws SerializationException, IOException, JSONException {
//
Questionnaire questionnaire = new Questionnaire();
questionnaire.setId("questionnaire-id");
//
Roundabout roundabout = new Roundabout();
roundabout.setId("roundabout-id");
roundabout.setPage("1");
roundabout.setConditionFilter(new ConditionFilterType());
roundabout.getConditionFilter().setValue("true");
roundabout.getConditionFilter().setType(LabelTypeEnum.VTL);
roundabout.setLabel(createLabel("\"Roundabout label.\"", LabelTypeEnum.VTL));
roundabout.setIterations(createLabel("NUMBER_VAR", LabelTypeEnum.VTL));
roundabout.setLocked(true);
roundabout.setProgressVariable("PROGRESS");
Roundabout.Item roundaboutItem = new Roundabout.Item();
roundaboutItem.setLabel(createLabel("FIRST_NAME", LabelTypeEnum.VTL));
roundaboutItem.setDescription(createLabel(
"if AGE > 18 then \"Questions for \" || FIRST_NAME else FIRST_NAME || \" is not concerned\"",
LabelTypeEnum.VTL));
roundaboutItem.setDisabled(createLabel("AGE < 18", LabelTypeEnum.VTL));
roundabout.setItem(roundaboutItem);
Input input = new Input();
input.setId("input-id");
roundabout.getComponents().add(input);
//
questionnaire.getComponents().add(roundabout);

//
String result = new JsonSerializer().serialize(questionnaire);

//
String expectedJson = TestUtils.readResourceFile("roundabout.json");
JSONAssert.assertEquals(result, expectedJson, JSONCompareMode.STRICT);
}

@Test
void deserializeRoundabout() throws IOException, SerializationException {
//
String jsonInput = TestUtils.readResourceFile("roundabout.json");

//
Questionnaire questionnaire = new JsonDeserializer().deserialize(
new ByteArrayInputStream(jsonInput.getBytes()));

//
Roundabout roundabout = assertInstanceOf(Roundabout.class, questionnaire.getComponents().getFirst());
assertEquals(1, roundabout.getComponents().size());
assertEquals(ComponentTypeEnum.INPUT, roundabout.getComponents().getFirst().getComponentType());
}

}
14 changes: 14 additions & 0 deletions src/test/java/fr/insee/lunatic/utils/TestUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package fr.insee.lunatic.utils;

import fr.insee.lunatic.model.flat.LabelType;
import fr.insee.lunatic.model.flat.LabelTypeEnum;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
Expand All @@ -26,4 +29,15 @@ public static String readResourceFile(String relativePath) throws IOException {
}
}

/**
* Utility method to create a label object in one line in tests.
* (Might be moved in main code if it is found useful to have it there.)
*/
public static LabelType createLabel(String value, LabelTypeEnum type) {
LabelType label = new LabelType();
label.setValue(value);
label.setType(type);
return label;
}

}
45 changes: 45 additions & 0 deletions src/test/resources/roundabout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"id": "questionnaire-id",
"componentType": "Questionnaire",
"components": [
{
"id": "roundabout-id",
"componentType": "Roundabout",
"page": "1",
"conditionFilter": {
"value": "true",
"type": "VTL"
},
"label": {
"value": "\"Roundabout label.\"",
"type": "VTL"
},
"iterations": {
"value": "NUMBER_VAR",
"type": "VTL"
},
"locked": true,
"progressVariable": "PROGRESS",
"item": {
"label": {
"value": "FIRST_NAME",
"type": "VTL"
},
"description": {
"value": "if AGE > 18 then \"Questions for \" || FIRST_NAME else FIRST_NAME || \" is not concerned\"",
"type": "VTL"
},
"disabled": {
"value": "AGE < 18",
"type": "VTL"
}
},
"components": [
{
"id": "input-id",
"componentType": "Input"
}
]
}
]
}

0 comments on commit 028afc8

Please sign in to comment.