Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,23 @@ class ExternalSelectsTest {
.assertText("File: $formsDirPath/search_and_select-media/nombre.csv is missing.")
.assertText("File: $formsDirPath/search_and_select-media/nombre2.csv is missing.")
}

@Test
fun dynamicChoicesCanBeMixedWithNumericInternalOnes() {
rule.startAtMainMenu()
.copyForm("dynamic_and_static_choices.xml", listOf("fruits.csv"))
.startBlankForm("dynamic_and_static_choices")
.assertTexts("Mango", "Oranges", "Strawberries", "Apples")
}

@Test
fun missingFileMessage_shouldBeDisplayedIfDynamicChoicesUsedButTheConfigurationRowIsMissing() {
val formsDirPath = StoragePathProvider().getOdkDirPath(StorageSubdirectory.FORMS)

rule.startAtMainMenu()
.copyForm("dynamic_and_static_choices.xml", listOf("fruits.csv"))
.startBlankForm("dynamic_and_static_choices")
.swipeToNextQuestion("Choose a number")
.assertText("File: $formsDirPath/dynamic_and_static_choices-media/numbers.csv is missing.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ public static ArrayList<SelectChoice> populateExternalChoices(FormEntryPrompt fo
XPathFuncExpr xpathfuncexpr, FormController formController) throws FileNotFoundException {
try {
List<SelectChoice> selectChoices = formEntryPrompt.getSelectChoices();
if (containsConfigurationChoice(selectChoices)) {
String filePath = getFilePath(xpathfuncexpr, formController);
throw new FileNotFoundException(filePath);
}
ArrayList<SelectChoice> returnedChoices = new ArrayList<>();
for (SelectChoice selectChoice : selectChoices) {
String value = selectChoice.getValue();
Expand Down Expand Up @@ -212,14 +216,7 @@ public static ArrayList<SelectChoice> populateExternalChoices(FormEntryPrompt fo
}
return returnedChoices;
} catch (Exception e) {
String fileName = String.valueOf(xpathfuncexpr.args[0].eval(null, null));
if (!fileName.endsWith(".csv")) {
fileName = fileName + ".csv";
}
String filePath = fileName;
if (formController != null) {
filePath = formController.getMediaFolder() + File.separator + fileName;
}
String filePath = getFilePath(xpathfuncexpr, formController);
if (!new File(filePath).exists()) {
throw new FileNotFoundException(filePath);
}
Expand Down Expand Up @@ -345,4 +342,30 @@ public static boolean isAnInteger(String value) {
return false;
}
}

/**
* The config row is a special row in the choices worksheet that defines how
* choices are mapped from an external .csv file with list_name, name and label,
* where these correspond to columns in the .csv file.
*/
private static boolean containsConfigurationChoice(List<SelectChoice> selectChoices) {
for (SelectChoice choice : selectChoices) {
if (!isAnInteger(choice.getValue())) {
return false;
}
}
return true;
}

private static String getFilePath(XPathFuncExpr xpathfuncexpr, FormController formController) {
String fileName = String.valueOf(xpathfuncexpr.args[0].eval(null, null));
if (!fileName.endsWith(".csv")) {
fileName = fileName + ".csv";
}
String filePath = fileName;
if (formController != null) {
filePath = formController.getMediaFolder() + File.separator + fileName;
}
return filePath;
}
}
51 changes: 51 additions & 0 deletions test-forms/src/main/resources/forms/dynamic_and_static_choices.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0"?>
<h:html
xmlns="http://www.w3.org/2002/xforms"
xmlns:h="http://www.w3.org/1999/xhtml"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jr="http://openrosa.org/javarosa"
xmlns:orx="http://openrosa.org/xforms"
xmlns:odk="http://www.opendatakit.org/xforms">
<h:head>
<h:title>dynamic_and_static_choices</h:title>
<model odk:xforms-version="1.0.0">
<instance>
<data id="dynamic_and_static_choices">
<fruits/>
<numbers/>
<meta>
<instanceID/>
</meta>
</data>
</instance>
<bind nodeset="/data/fruits" type="string"/>
<bind nodeset="/data/numbers" type="string"/>
<bind nodeset="/data/meta/instanceID" type="string" readonly="true()" jr:preload="uid"/>
</model>
</h:head>
<h:body>
<select1 ref="/data/fruits" appearance="search('fruits')">
<label>Choose a fruit</label>
<item>
<label>name</label>
<value>name_key</value>
</item>
<item>
<label>Apples</label>
<value>0</value>
</item>
</select1>
<select1 ref="/data/numbers" appearance="search('numbers')">
<label>Choose a number</label>
<item>
<label>0</label>
<value>0</value>
</item>
<item>
<label>1</label>
<value>1</value>
</item>
</select1>
</h:body>
</h:html>