diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index 7aa55ea3..d301d158 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -21,7 +21,6 @@ if TYPE_CHECKING: from typing import Any, Dict, KeysView, List, Optional - SMART_QUOTES = {"\u2018": "'", "\u2019": "'", "\u201c": '"', "\u201d": '"'} _MSG_SUPPRESS_SPELLING = ( " If you do not mean to include a sheet, to suppress this message, " @@ -1287,9 +1286,16 @@ def workbook_to_json( if question_type in ["geopoint", "geoshape", "geotrace"]: new_dict = row.copy() - parameters = get_parameters( - row.get("parameters", ""), ["allow-mock-accuracy"] - ) + + if question_type == "geopoint": + parameters = get_parameters( + row.get("parameters", ""), + ["allow-mock-accuracy", "capture-accuracy", "warning-accuracy"], + ) + else: + parameters = get_parameters( + row.get("parameters", ""), ["allow-mock-accuracy"] + ) if "allow-mock-accuracy" in parameters.keys(): if parameters["allow-mock-accuracy"] not in ["true", "false"]: @@ -1300,6 +1306,29 @@ def workbook_to_json( {"odk:allow-mock-accuracy": parameters["allow-mock-accuracy"]} ) + new_dict["control"] = new_dict.get("control", {}) + if "capture-accuracy" in parameters.keys(): + try: + float(parameters["capture-accuracy"]) + new_dict["control"].update( + {"accuracyThreshold": parameters["capture-accuracy"]} + ) + except ValueError: + raise PyXFormError( + "Parameter capture-accuracy must have a numeric value" + ) + + if "warning-accuracy" in parameters.keys(): + try: + float(parameters["warning-accuracy"]) + new_dict["control"].update( + {"unacceptableAccuracyThreshold": parameters["warning-accuracy"]} + ) + except ValueError: + raise PyXFormError( + "Parameter warning-accuracy must have a numeric value" + ) + parent_children_array.append(new_dict) continue diff --git a/tests/test_allow_mock_accuracy.py b/tests/test_allow_mock_accuracy.py index 09b6a4ec..5e256f7c 100644 --- a/tests/test_allow_mock_accuracy.py +++ b/tests/test_allow_mock_accuracy.py @@ -2,8 +2,8 @@ from tests.pyxform_test_case import PyxformTestCase -class AllowMockAccuracyTest(PyxformTestCase): - def test_geopoint(self): +class GeoParameterTest(PyxformTestCase): + def test_geopoint_allow_mock_accuracy(self): self.assertPyxformXform( name="data", md=""" @@ -30,7 +30,7 @@ def test_geopoint(self): ], ) - def test_geoshape(self): + def test_geoshape_allow_mock_accuracy(self): self.assertPyxformXform( name="data", md=""" @@ -57,7 +57,7 @@ def test_geoshape(self): ], ) - def test_geotrace(self): + def test_geotrace_allow_mock_accuracy(self): self.assertPyxformXform( name="data", md=""" @@ -84,7 +84,7 @@ def test_geotrace(self): ], ) - def test_foo_fails(self): + def test_foo_allow_mock_accuracy_value_fails(self): self.assertPyxformXform( name="data", md=""" @@ -117,3 +117,91 @@ def test_foo_fails(self): errored=True, error__contains=["Invalid value for allow-mock-accuracy."], ) + + def test_numeric_geopoint_capture_accuracy_is_passed_through(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geopoint | geopoint | Geopoint | capture-accuracy=2.5 | + """, + xml__xpath_match=[ + "/h:html/h:body/x:input[@accuracyThreshold='2.5' and @ref='/data/geopoint']" + ], + ) + + def test_string_geopoint_capture_accuracy_errors(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geopoint | geopoint | Geopoint | capture-accuracy=foo | + """, + errored=True, + error__contains=["Parameter capture-accuracy must have a numeric value"], + ) + + def test_geopoint_warning_accuracy_is_passed_through(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geopoint | geopoint | Geopoint | warning-accuracy=5 | + """, + xml__xpath_match=[ + "/h:html/h:body/x:input[@unacceptableAccuracyThreshold='5' and @ref='/data/geopoint']" + ], + ) + + def test_string_geopoint_warning_accuracy_errors(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geopoint | geopoint | Geopoint | warning-accuracy=foo | + """, + errored=True, + error__contains=["Parameter warning-accuracy must have a numeric value"], + ) + + def test_geopoint_parameters_combine(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geopoint | geopoint | Geopoint | warning-accuracy=5.5 capture-accuracy=2 allow-mock-accuracy=true | + """, + xml__xpath_match=[ + "/h:html/h:body/x:input[@unacceptableAccuracyThreshold='5.5' and @accuracyThreshold='2' and @ref='/data/geopoint']", + "/h:html/h:head/x:model/x:bind[@nodeset='/data/geopoint' and @odk:allow-mock-accuracy='true']", + ], + ) + + def test_geoshape_with_accuracy_parameters_errors(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geoshape | geoshape | Geoshape | warning-accuracy=5 | + """, + errored=True, + error__contains=["'warning-accuracy' is an invalid parameter"], + ) + + def test_geotrace_with_accuracy_parameters_errors(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | geotrace | geotrace | Geotrace | warning-accuracy=5 | + """, + errored=True, + error__contains=["'warning-accuracy' is an invalid parameter"], + )