Skip to content

Commit 7eebdd4

Browse files
authored
fix(exports): convert bool values for SAV exports (#3102)
1 parent c3ed685 commit 7eebdd4

2 files changed

Lines changed: 79 additions & 3 deletions

File tree

onadata/libs/tests/utils/test_export_builder.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,75 @@ def test_zipped_sav_export_with_date_field(self):
727727

728728
shutil.rmtree(temp_dir)
729729

730+
def test_zipped_sav_export_with_bool_string_value(self):
731+
"""Boolean values in string fields are exported as strings."""
732+
md = """
733+
| survey |
734+
| | type | name | label |
735+
| | text | active | Active |
736+
737+
| choices |
738+
| | list name | name | label |
739+
"""
740+
survey = self.md_to_pyxform_survey(md, {"name": "exp"})
741+
data = [{"active": True}]
742+
export_builder = ExportBuilder()
743+
export_builder.set_survey(survey)
744+
with NamedTemporaryFile(suffix=".zip") as temp_zip_file:
745+
export_builder.to_zipped_sav(temp_zip_file.name, data)
746+
temp_zip_file.seek(0)
747+
temp_dir = tempfile.mkdtemp()
748+
with zipfile.ZipFile(temp_zip_file.name, "r") as zip_file:
749+
zip_file.extractall(temp_dir)
750+
751+
self.assertTrue(os.path.exists(os.path.join(temp_dir, "exp.sav")))
752+
753+
with SavReader(os.path.join(temp_dir, "exp.sav"), returnHeader=True) as reader:
754+
rows = list(reader)
755+
self.assertTrue(len(rows) > 1)
756+
self.assertEqual(_str_if_bytes(rows[0][0]), "active")
757+
self.assertEqual(_str_if_bytes(rows[1][0]), "True")
758+
759+
shutil.rmtree(temp_dir)
760+
761+
def test_zipped_sav_export_with_split_select_multiple_bool_values(self):
762+
"""Split select multiple bool values are exported correctly."""
763+
md = """
764+
| survey |
765+
| | type | name | label |
766+
| | select_multiple food | food_available | Food |
767+
768+
| choices |
769+
| | list name | name | label |
770+
| | food | 1 | One |
771+
| | food | 2 | Two |
772+
"""
773+
survey = self.md_to_pyxform_survey(md, {"name": "exp"})
774+
data = [{"food_available": "1"}]
775+
export_builder = ExportBuilder()
776+
export_builder.set_survey(survey)
777+
with NamedTemporaryFile(suffix=".zip") as temp_zip_file:
778+
export_builder.to_zipped_sav(temp_zip_file.name, data)
779+
temp_zip_file.seek(0)
780+
temp_dir = tempfile.mkdtemp()
781+
with zipfile.ZipFile(temp_zip_file.name, "r") as zip_file:
782+
zip_file.extractall(temp_dir)
783+
784+
self.assertTrue(os.path.exists(os.path.join(temp_dir, "exp.sav")))
785+
786+
with SavReader(os.path.join(temp_dir, "exp.sav"), returnHeader=True) as reader:
787+
rows = list(reader)
788+
rows[0] = list(map(_str_if_bytes, rows[0]))
789+
self.assertTrue(len(rows) > 1)
790+
self.assertEqual(rows[0][0], "food_available")
791+
self.assertEqual(_str_if_bytes(rows[1][0]), "1")
792+
self.assertEqual(rows[0][1], "food_available.1")
793+
self.assertEqual(rows[1][1], 1.0)
794+
self.assertEqual(rows[0][2], "food_available.2")
795+
self.assertEqual(rows[1][2], 0.0)
796+
797+
shutil.rmtree(temp_dir)
798+
730799
# pylint: disable=invalid-name
731800
def test_zipped_sav_export_dynamic_select_multiple(self):
732801
md = """

onadata/libs/utils/export_builder.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,9 +1440,16 @@ def to_zipped_sav(self, path, data, *args, **kwargs):
14401440
def write_row(row, sav_writer, fields):
14411441
# replace character for osm fields
14421442
fields = [field.replace(":", "_") for field in fields]
1443-
sav_writer.writerow(
1444-
[encode_if_str(row, field, sav_writer=sav_writer) for field in fields]
1445-
)
1443+
record = []
1444+
for field, var_name in zip(fields, sav_writer.varNames):
1445+
value = encode_if_str(row, field, sav_writer=sav_writer)
1446+
if (
1447+
isinstance(value, bool)
1448+
and sav_writer.varTypes[var_name] != SAV_NUMERIC_TYPE
1449+
):
1450+
value = str(value)
1451+
record.append(value)
1452+
sav_writer.writerow(record)
14461453

14471454
sav_defs = {}
14481455

0 commit comments

Comments
 (0)