Skip to content

Commit 983b990

Browse files
add: error if entity save_to in undeclared repeat
- in this case a top-level entity instance would be generated and so it wouldn't be populated properly by the item in the repeat. Either a mistake in where the save_to was placed, or the entity repeat column was left blank by mistake.
1 parent 0014ca9 commit 983b990

3 files changed

Lines changed: 52 additions & 21 deletions

File tree

pyxform/entities/entities_parsing.py

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,26 @@
4141
name="Invalid entity repeat save_to: question in nested repeat",
4242
msg=(
4343
"[row : {row}] On the 'survey' sheet, the 'save_to' value '{value}' is invalid. "
44-
"The entity properties populated with 'save_to' must not be inside of a nested "
44+
"The entity property populated with 'save_to' must not be inside of a nested "
4545
"repeat within the entity repeat."
4646
),
4747
)
4848
ENTITY006 = Detail(
4949
name="Invalid entity repeat save_to: question not in entity repeat",
5050
msg=(
5151
"[row : {row}] On the 'survey' sheet, the 'save_to' value '{value}' is invalid. "
52-
"The entity properties populated with 'save_to' must be inside of the entity "
52+
"The entity property populated with 'save_to' must be inside of the entity "
5353
"repeat."
5454
),
5555
)
56+
ENTITY007 = Detail(
57+
name="Invalid entity repeat save_to: question in repeat but no entity repeat defined",
58+
msg=(
59+
"[row : {row}] On the 'survey' sheet, the 'save_to' value '{value}' is invalid. "
60+
"The entity property populated with 'save_to' must be inside a repeat that is "
61+
"declared in the 'repeat' column of the 'entities' sheet."
62+
),
63+
)
5664

5765

5866
def get_entity_declaration(
@@ -244,8 +252,8 @@ def get_validated_repeat_name(entity) -> str | None:
244252
def validate_entity_saveto(
245253
row: dict,
246254
row_number: int,
255+
stack: Sequence[dict[str, Any]],
247256
entity_declaration: dict[str, Any] | None = None,
248-
stack: Sequence[dict[str, Any]] | None = None,
249257
):
250258
save_to = row.get(const.BIND, {}).get("entities:saveto", "")
251259
if not save_to:
@@ -262,23 +270,26 @@ def validate_entity_saveto(
262270
)
263271

264272
entity_repeat = entity_declaration.get(EC.REPEAT, None)
265-
if entity_repeat and stack:
266-
# Error: saveto in nested repeat inside entity repeat.
267-
in_repeat = False
268-
located = False
269-
for i in reversed(stack):
270-
if not i["control_name"] or not i["control_type"]:
271-
break
272-
elif i["control_type"] == const.REPEAT:
273-
if in_repeat:
274-
raise PyXFormError(ENTITY005.format(row=row_number, value=save_to))
275-
elif i["control_name"] == entity_repeat:
276-
located = True
277-
in_repeat = True
278-
279-
# Error: saveto not in entity repeat
280-
if not located:
281-
raise PyXFormError(ENTITY006.format(row=row_number, value=save_to))
273+
in_repeat = False
274+
located = False
275+
for i in reversed(stack):
276+
if not i["control_name"] or not i["control_type"]:
277+
break
278+
elif i["control_type"] == const.REPEAT:
279+
# Error: saveto in nested repeat inside entity repeat.
280+
if in_repeat:
281+
raise PyXFormError(ENTITY005.format(row=row_number, value=save_to))
282+
elif i["control_name"] == entity_repeat:
283+
located = True
284+
in_repeat = True
285+
286+
# Error: saveto not in entity repeat
287+
if entity_repeat and not located:
288+
raise PyXFormError(ENTITY006.format(row=row_number, value=save_to))
289+
290+
# Error: saveto in repeat but no entity repeat declared
291+
if in_repeat and not entity_repeat:
292+
raise PyXFormError(ENTITY007.format(row=row_number, value=save_to))
282293

283294
error_start = f"{const.ROW_FORMAT_STRING % row_number} Invalid save_to name:"
284295

pyxform/xls2json.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,8 @@ def workbook_to_json(
861861
validate_entity_saveto(
862862
row=row,
863863
row_number=row_number,
864-
entity_declaration=entity_declaration,
865864
stack=stack,
865+
entity_declaration=entity_declaration,
866866
)
867867

868868
# Try to parse question as begin control statement

tests/entities/test_create_survey.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pyxform import constants as co
2+
from pyxform.entities import entities_parsing as ep
23

34
from tests.pyxform_test_case import PyxformTestCase
45
from tests.xpath_helpers.entities import xpe
@@ -345,6 +346,25 @@ def test_saveto_on_group__errors(self):
345346
],
346347
)
347348

349+
def test_saveto_in_repeat__errors(self):
350+
"""Should find that an error is raised if a save_to is in an undeclared entity repeat."""
351+
md = """
352+
| survey |
353+
| | type | name | label | save_to |
354+
| | begin_repeat | r1 | R1 | |
355+
| | text | q1 | Q1 | q1e |
356+
| | end_repeat | r1 | | |
357+
358+
| entities |
359+
| | dataset | label |
360+
| | trees | ${q1} |
361+
"""
362+
self.assertPyxformXform(
363+
md=md,
364+
errored=True,
365+
error__contains=[ep.ENTITY007.format(row=3, value="q1e")],
366+
)
367+
348368
def test_saveto_in_group__works(self):
349369
self.assertPyxformXform(
350370
name="data",

0 commit comments

Comments
 (0)