Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 10 additions & 4 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,13 @@ jobs:
matrix:
python: ['3.10', '3.11', '3.12', '3.13']
os: [ubuntu-latest, macos-latest, windows-latest]
PYXFORM_TESTS_RUN_ODK_VALIDATE: ['false']
# Extra job to check PyxformTestCase tests pass with ODK Validate.
# Keep versions sync with default dev version (same as 'lint' job above).
include:
- os: windows-latest
windows_nose_args: --traverse-namespace ./tests
- python: '3.12'
os: ubuntu-latest
PYXFORM_TESTS_RUN_ODK_VALIDATE: 'true'
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand All @@ -67,15 +71,17 @@ jobs:
# Tests.
- name: Run tests
run: python -m unittest --verbose
env:
PYXFORM_TESTS_RUN_ODK_VALIDATE: ${{ matrix.PYXFORM_TESTS_RUN_ODK_VALIDATE }}

# Build and Upload.
- name: Build sdist and wheel.
if: success()
if: success() && matrix.PYXFORM_TESTS_RUN_ODK_VALIDATE == 'false'
run: |
pip install flit==3.9.0
flit --debug build --no-use-vcs
- name: Upload sdist and wheel.
if: success()
if: success() && matrix.PYXFORM_TESTS_RUN_ODK_VALIDATE == 'false'
uses: actions/upload-artifact@v4
with:
name: pyxform--on-${{ matrix.os }}--py${{ matrix.python }}
Expand Down
5 changes: 4 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ Releasing pyxform

pyxform_validator_update odk update ODK-Validate-vx.x.x.jar

2. Run all tests through Validate by setting the default for ``run_odk_validate`` to ``True`` in ``tests/pyxform_test_case.py``.
2. Run all tests through ODK Validate as follows:

PYXFORM_TESTS_RUN_ODK_VALIDATE=true python -m unittest --verbose

3. Draft a new GitHub release with the list of merged PRs. Follow the title and description pattern of the previous release.
4. Checkout a release branch from latest upstream master.
5. Update ``CHANGES.txt`` with the text of the draft release.
Expand Down
4 changes: 2 additions & 2 deletions pyxform/entities/entity_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _get_id_bind_node(self, survey, entity_id_expression):

if entity_id_expression:
id_bind["calculate"] = survey.insert_xpaths(
entity_id_expression, context=self
text=entity_id_expression, context=self
)

return node(const.BIND, nodeset=self.get_xpath() + "/@id", **id_bind)
Expand All @@ -133,7 +133,7 @@ def _get_id_setvalue_node(self):
return node("setvalue", ref=self.get_xpath() + "/@id", **id_setvalue_attrs)

def _get_bind_node(self, survey, expression, destination):
expr = survey.insert_xpaths(expression, context=self)
expr = survey.insert_xpaths(text=expression, context=self)
bind_attrs = {
"calculate": expr,
const.TYPE: "string",
Expand Down
19 changes: 13 additions & 6 deletions pyxform/question.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def xml_instance(self, survey: "Survey", **kwargs):
attributes = self.instance
if attributes:
for k, v in attributes.items():
result.setAttribute(k, survey.insert_xpaths(v, self))
result.setAttribute(k, survey.insert_xpaths(text=v, context=self))
return result

def xml_control(self, survey: "Survey"):
Expand Down Expand Up @@ -200,7 +200,7 @@ def nest_set_nodes(self, survey, xml_node, tag, nested_items):
"event": "xforms-value-changed",
}
if item[1]:
node_attrs["value"] = survey.insert_xpaths(item[1], self)
node_attrs["value"] = survey.insert_xpaths(text=item[1], context=self)
set_node = node(tag, **node_attrs)
xml_node.appendChild(set_node)

Expand All @@ -219,7 +219,7 @@ def _build_xml(self, survey: "Survey") -> DetachableElement | None:
# "tag" is from the question type dict so it can't include references. Also,
# if it did include references, then the node element name would be invalid.
if k != "tag":
result.setAttribute(k, survey.insert_xpaths(v, self))
result.setAttribute(k, survey.insert_xpaths(text=v, context=self))
return result

def build_xml(self, survey: "Survey") -> DetachableElement | None:
Expand Down Expand Up @@ -269,7 +269,9 @@ def build_xml(self, survey: "Survey"):
if self.query:
choice_filter = self.choice_filter
if choice_filter:
pred = survey.insert_xpaths(choice_filter, self, True)
pred = survey.insert_xpaths(
text=choice_filter, context=self, use_current=True
)
query = f"""instance('{self.query}')/root/item[{pred}]"""
else:
query = f"""instance('{self.query}')/root/item"""
Expand Down Expand Up @@ -417,11 +419,16 @@ def build_xml(self, survey: "Survey"):
choice_filter = self.choice_filter
if choice_filter:
choice_filter = survey.insert_xpaths(
choice_filter, self, True, is_previous_question
text=choice_filter,
context=self,
use_current=True,
reference_parent=is_previous_question,
)
if is_previous_question:
path = (
survey.insert_xpaths(self.itemset, self, reference_parent=True)
survey.insert_xpaths(
text=self.itemset, context=self, reference_parent=True
)
.strip()
.split("/")
)
Expand Down
76 changes: 62 additions & 14 deletions pyxform/section.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def xml_instance(self, survey: "Survey", **kwargs):
attributes.update(self.instance)
# Resolve field references in attributes
for key, value in attributes.items():
attributes[key] = survey.insert_xpaths(value, self)
attributes[key] = survey.insert_xpaths(text=value, context=self)
result = node(self.name, **attributes)

for child in self.children:
Expand Down Expand Up @@ -173,6 +173,36 @@ def xml_control(self, survey: "Survey"):


class RepeatingSection(Section):
def __init__(
self,
name: str,
type: str = constants.REPEAT,
label: str | dict | None = None,
hint: str | dict | None = None,
bind: dict | None = None,
control: dict | None = None,
instance: dict | None = None,
media: dict | None = None,
flat: bool | None = None,
sms_field: str | None = None,
fields: tuple[str, ...] | None = None,
**kwargs,
):
super().__init__(
name=name,
type=type,
label=label,
hint=hint,
bind=bind,
control=control,
instance=instance,
media=media,
flat=flat,
sms_field=sms_field,
fields=fields,
**kwargs,
)

def xml_control(self, survey: "Survey"):
"""
<group>
Expand All @@ -190,7 +220,7 @@ def xml_control(self, survey: "Survey"):
# Resolve field references in attributes
if self.control:
control_dict = {
key: survey.insert_xpaths(value, self)
key: survey.insert_xpaths(text=value, context=self)
for key, value in self.control.items()
}
repeat_node = node("repeat", nodeset=self.get_xpath(), **control_dict)
Expand Down Expand Up @@ -233,17 +263,35 @@ def template_instance(self, survey: "Survey", **kwargs):


class GroupedSection(Section):
# I think this might be a better place for the table-list stuff, however it
# doesn't allow for as good of validation as putting it in xls2json
# def __init__(self, **kwargs):
# control = kwargs.get(u"control")
# if control:
# appearance = control.get(u"appearance")
# if appearance is u"table-list":
# print "HI"
# control[u"appearance"] = "field-list"
# kwargs["children"].insert(0, kwargs["children"][0])
# super(GroupedSection, self).__init__(kwargs)
def __init__(
self,
name: str,
type: str = constants.GROUP,
label: str | dict | None = None,
hint: str | dict | None = None,
bind: dict | None = None,
control: dict | None = None,
instance: dict | None = None,
media: dict | None = None,
flat: bool | None = None,
sms_field: str | None = None,
fields: tuple[str, ...] | None = None,
**kwargs,
):
super().__init__(
name=name,
type=type,
label=label,
hint=hint,
bind=bind,
control=control,
instance=instance,
media=media,
flat=flat,
sms_field=sms_field,
fields=fields,
**kwargs,
)

def xml_control(self, survey: "Survey"):
if self.control and self.control.get("bodyless"):
Expand All @@ -254,7 +302,7 @@ def xml_control(self, survey: "Survey"):
# Resolve field references in attributes
if self.control:
attributes = {
key: survey.insert_xpaths(value, self)
key: survey.insert_xpaths(text=value, context=self)
for key, value in self.control.items()
}
if "appearance" in self.control:
Expand Down
Loading
Loading