Skip to content

Commit

Permalink
feat: duration component
Browse files Browse the repository at this point in the history
  • Loading branch information
nsenave committed Apr 29, 2024
1 parent 73dde36 commit 48c8b9b
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 2 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.7.0</version>
<version>3.8.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
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
@JsonSubTypes.Type(value = Input.class, name = "Input"),
@JsonSubTypes.Type(value = PairwiseLinks.class, name = "PairwiseLinks"),
@JsonSubTypes.Type(value = Datepicker.class, name = "Datepicker"),
@JsonSubTypes.Type(value = Duration.class, name = "Duration"),
@JsonSubTypes.Type(value = CheckboxGroup.class, name = "CheckboxGroup"),
@JsonSubTypes.Type(value = CheckboxOne.class, name = "CheckboxOne"),
@JsonSubTypes.Type(value = CheckboxBoolean.class, name = "CheckboxBoolean"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum ComponentTypeEnum {
PAIRWISE_LINKS("PairwiseLinks"),
INPUT_NUMBER("InputNumber"),
DATEPICKER("Datepicker"),
DURATION("Duration"),
CHECKBOX_GROUP("CheckboxGroup"),
CHECKBOX_ONE("CheckboxOne"),
CHECKBOX_BOOLEAN("CheckboxBoolean"),
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/fr/insee/lunatic/model/flat/Duration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package fr.insee.lunatic.model.flat;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Getter
@Slf4j
public class Duration extends ComponentType implements ComponentSimpleResponseType {

/** Value for a years/months duration format. */
public static final String YEARS_MONTHS_FORMAT = "PnYnM";

/** Value for a hours/minutes duration format. */
public static final String HOURS_MINUTES_FORMAT = "PTnHnM";

public Duration() {
this.componentType = ComponentTypeEnum.DURATION;
}

/** Duration format in the XSD Duration Data Type style.
* Must start with a 'P' (that stands for period). Then can be followed by 'nY' (years), 'nM' (months), 'nD' (days).
* 'T' indicates the start of a time section that can be followed by 'nH' (hours), 'nM' (minutes), 'nS' (seconds). */
@JsonProperty(required = true)
private String format;

@JsonProperty(required = true)
@Setter
protected ResponseType response;

/**
* Sets the duration format. Warning: this method doesn't do any strict validation.
* @param format Format in the XSD Duration Data Type style.
*/
public void setFormat(String format) {
if (! (YEARS_MONTHS_FORMAT.equals(format) || HOURS_MINUTES_FORMAT.equals(format)))
log.warn("Format '{}' does not match Lunatic commonly-used formats.", format);
this.format = format;
}

}
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 @@ -56,7 +56,7 @@ public static boolean isQuestionComponent(ComponentType component) {
if (component.getComponentType() == null)
return false;
return switch (component.getComponentType()) {
case CHECKBOX_BOOLEAN, INPUT, TEXTAREA, INPUT_NUMBER, DATEPICKER,
case CHECKBOX_BOOLEAN, INPUT, TEXTAREA, INPUT_NUMBER, DATEPICKER, DURATION,
CHECKBOX_ONE, RADIO, DROPDOWN, SUGGESTER,
CHECKBOX_GROUP, TABLE, ROSTER_FOR_LOOP, PAIRWISE_LINKS -> true;
case QUESTIONNAIRE, SEQUENCE, SUBSEQUENCE, QUESTION, LOOP -> false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
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 org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;

class DurationSerializationTest {

@Test
void serializeDuration_yearsMonthsFormat() throws SerializationException, IOException, JSONException {
//
Questionnaire questionnaire = new Questionnaire();
questionnaire.setId("questionnaire-id");
//
Duration duration = new Duration();
duration.setId("duration-id");
duration.setFormat(Duration.YEARS_MONTHS_FORMAT);
duration.setLabel(new LabelType());
duration.getLabel().setValue("\"Duration (years/months)\"");
duration.getLabel().setType(LabelTypeEnum.VTL_MD);
duration.setDescription(new LabelType());
duration.getDescription().setValue("\"2 years and 5 months\"");
duration.getDescription().setType(LabelTypeEnum.VTL_MD);
duration.setResponse(new ResponseType());
duration.getResponse().setName("DURATION_VAR");
//
questionnaire.getComponents().add(duration);

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

String expectedJson = TestUtils.readResourceFile("duration-years-months.json");
JSONAssert.assertEquals(expectedJson, result, JSONCompareMode.STRICT);
}

@Test
void serializeDuration_HoursMinutesFormat() throws SerializationException, IOException, JSONException {
//
Questionnaire questionnaire = new Questionnaire();
questionnaire.setId("questionnaire-id");
//
Duration duration = new Duration();
duration.setId("duration-id");
duration.setFormat(Duration.HOURS_MINUTES_FORMAT);
duration.setLabel(new LabelType());
duration.getLabel().setValue("\"Duration (hours/minutes)\"");
duration.getLabel().setType(LabelTypeEnum.VTL_MD);
duration.setDescription(new LabelType());
duration.getDescription().setValue("\"1 hour and 10 minutes\"");
duration.getDescription().setType(LabelTypeEnum.VTL_MD);
duration.setResponse(new ResponseType());
duration.getResponse().setName("DURATION_VAR");
//
questionnaire.getComponents().add(duration);

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

String expectedJson = TestUtils.readResourceFile("duration-hours-minutes.json");
JSONAssert.assertEquals(expectedJson, result, JSONCompareMode.STRICT);
}

@Test
void deserializeDuration_yearsMonthsFormat() throws IOException, SerializationException {
//
String jsonInput = TestUtils.readResourceFile("duration-years-months.json");
//
Questionnaire questionnaire = new JsonDeserializer().deserialize(new ByteArrayInputStream(jsonInput.getBytes()));
//
Duration duration = assertInstanceOf(Duration.class, questionnaire.getComponents().getFirst());
assertEquals("PnYnM", duration.getFormat());
}

@Test
void deserializeDuration_hoursMinutesFormat() throws IOException, SerializationException {
//
String jsonInput = TestUtils.readResourceFile("duration-hours-minutes.json");
//
Questionnaire questionnaire = new JsonDeserializer().deserialize(new ByteArrayInputStream(jsonInput.getBytes()));
//
Duration duration = assertInstanceOf(Duration.class, questionnaire.getComponents().getFirst());
assertEquals("PTnHnM", duration.getFormat());
}

}
22 changes: 22 additions & 0 deletions src/test/resources/duration-hours-minutes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"id": "questionnaire-id",
"componentType": "Questionnaire",
"components": [
{
"id": "duration-id",
"componentType": "Duration",
"format": "PTnHnM",
"label": {
"value": "\"Duration (hours/minutes)\"",
"type": "VTL|MD"
},
"description": {
"value": "\"1 hour and 10 minutes\"",
"type": "VTL|MD"
},
"response": {
"name": "DURATION_VAR"
}
}
]
}
22 changes: 22 additions & 0 deletions src/test/resources/duration-years-months.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"id": "questionnaire-id",
"componentType": "Questionnaire",
"components": [
{
"id": "duration-id",
"componentType": "Duration",
"format": "PnYnM",
"label": {
"value": "\"Duration (years/months)\"",
"type": "VTL|MD"
},
"description": {
"value": "\"2 years and 5 months\"",
"type": "VTL|MD"
},
"response": {
"name": "DURATION_VAR"
}
}
]
}

0 comments on commit 48c8b9b

Please sign in to comment.