Change the parameters field/column default type to type dict
ukanga opened this issue · 2 comments
Software and hardware versions
pyxform v1.9
Problem description
When you process an XLSForm, in whatever format (markdown, spreadsheet file, e.t.c.), and get the JSON survey form via survey.to_json()
command. If you create a new survey object using this JSON representation, which is possible via the SurveyElementBuilder().create_survey_element_from_json(json_survey_dict)
, the parameters
field for a select one question that results in itemsets will be set to an empty string due to the str
default type of the parameters field here . This is the case for all XLSForms that do not have a parameters
values set for any field in the form. As such, the empty str default will be set here and as a result fail with the exception AttributeError: 'str' object has no attribute 'get'
when generating the XForm XML via SurveyElementBuilder().create_survey_element_from_json(survey.to_json()).to_xml()
.
Traceback (most recent call last):
...
json_survey = xls2json.parse_file_to_json(xlsform_path, warnings=warnings)
survey = builder.create_survey_element_from_dict(json_survey)
SurveyElementBuilder().create_survey_element_from_json(survey.to_json()).to_xml()
File "/.../pyxform/pyxform/survey.py", line 1117, in to_xml
self.print_xform_to_file(
File "/.../pyxform/pyxform/survey.py", line 1082, in print_xform_to_file
raise error
File "/.../pyxform/pyxform/survey.py", line 1076, in print_xform_to_file
file_obj.write(self._to_pretty_xml())
File "/.../pyxform/pyxform/survey.py", line 866, in _to_pretty_xml
xml_with_linebreaks = self.xml().toprettyxml(indent=" ")
File "/.../code/pyxform/pyxform/survey.py", line 254, in xml
node("h:body", *self.xml_control(), **body_kwargs),
File "/.../pyxform/pyxform/section.py", line 91, in xml_control
control = e.xml_control()
File "/.../pyxform/pyxform/question.py", line 55, in xml_control
xml_node = self.build_xml()
File "/.../pyxform/pyxform/question.py", line 210, in build_xml
itemset_value_ref = self.parameters.get(
AttributeError: 'str' object has no attribute 'get'
Steps to reproduce the problem
# create a survey object from an XLSForm File
json_survey = xls2json.parse_file_to_json(xlsform_path, warnings=warnings)
survey = builder.create_survey_element_from_dict(json_survey)
# test that you can successfully create the XForm XML of the survey object
survey.to_xml() # will not raise an exception
# creating survey from the JSON for the same survey even with no modifications will raise the AttributeError exception
SurveyElementBuilder().create_survey_element_from_json(survey.to_json()).to_xml() # AttributeError exception will be raised.
Expected behaviour
The JSON Survey representation should be able to create a Survey object that can generate the XForm XML without any exception being thrown.
Other information
I noticed that, when processing the XLSForm, or its JSON dict equivalent which is processed by workbook_to_json() always results in the parameters
field that has a dict empty or with key-value parameters. Hence the recommended fix is making the default value of type dict
which will not result in an exception. There seem to be no other scenarios, as far as I can tell, where the parameters
field value is a string when dealing with a SurveyElement
.
Why would you want to recreate a Survey object from the "XForm JSON representation"?
It is a cheap way to add some extra fields or metadata like setting the XForm version post processing the XLSForm file among other tasks an application could do for the user without the user making those changes themselves and if those changes are relevant to the application and not necessarily the user. Any other option better than this approach for this use case?
Before the change to set the parameters
default type to dict
.
======================================================================
ERROR: Re-using the same xml external data source id and URI is OK.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/.../pyxform/tests/test_external_instances.py", line 393, in test_can__reuse_xml__external_then_selects
SurveyElementBuilder().create_survey_element_from_json(survey.to_json()).to_xml()
File "/.../pyxform/pyxform/survey.py", line 1117, in to_xml
self.print_xform_to_file(
File "/.../pyxform/pyxform/survey.py", line 1082, in print_xform_to_file
raise error
File "/.../pyxform/pyxform/survey.py", line 1076, in print_xform_to_file
file_obj.write(self._to_pretty_xml())
File "/.../pyxform/pyxform/survey.py", line 866, in _to_pretty_xml
xml_with_linebreaks = self.xml().toprettyxml(indent=" ")
File "/.../pyxform/pyxform/survey.py", line 254, in xml
node("h:body", *self.xml_control(), **body_kwargs),
File "/.../pyxform/pyxform/section.py", line 91, in xml_control
control = e.xml_control()
File "/.../pyxform/pyxform/question.py", line 55, in xml_control
xml_node = self.build_xml()
File "/.../pyxform/pyxform/question.py", line 210, in build_xml
itemset_value_ref = self.parameters.get(
AttributeError: 'str' object has no attribute 'get'
Thanks for describing why you use this, that's really helpful!