Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
86e7479
chg: move NAMES001 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
c443766
chg: move NAMES002 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
b8c5265
chg: move NAMES003 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
15b2283
chg: move NAMES004 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
c33241e
chg: move NAMES005 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
c0312b1
chg: move choices.INVALID_NAME error into ErrorCode enum
lindsay-stevens Dec 16, 2025
7342b89
chg: move choices.INVALID_LABEL error into ErrorCode enum
lindsay-stevens Dec 16, 2025
dbeb2c1
chg: move choices.INVALID_DUPLICATE error into ErrorCode enum
lindsay-stevens Dec 16, 2025
0fa7cd9
chg: move sheet_headers.INVALID_HEADER error into ErrorCode enum
lindsay-stevens Dec 16, 2025
555a511
fix: tests providing single strings to error__contains
lindsay-stevens Dec 16, 2025
76c1bc9
chg: move sheet_headers.INVALID_DUPLICATE error into ErrorCode enum
lindsay-stevens Dec 16, 2025
f9a7c74
chg: move sheet_headers.INVALID_MISSING_REQUIRED error into ErrorCode
lindsay-stevens Dec 16, 2025
318ff0b
chg: move choices.INVALID_HEADER error into ErrorCode enum
lindsay-stevens Dec 16, 2025
f37d857
chg: sort ErrorCode enum
lindsay-stevens Dec 16, 2025
abc6d11
chg: move xls2json.SURVEY_001 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
c2969e3
chg: move xls2json.SURVEY_002 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
cd26763
chg: move entities_parsing.ENTITY_001 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
10e2dff
chg: move entities_parsing.ENTITY_002 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
cbe3a0d
chg: move entities_parsing.ENTITY_003 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
d498e59
chg: move entities_parsing.ENTITY_004 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
751a838
chg: move entities_parsing.ENTITY_005 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
6d18dc5
chg: move entities_parsing.ENTITY_006 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
3d3fb24
chg: move entities_parsing.ENTITY_007 error into ErrorCode enum
lindsay-stevens Dec 16, 2025
cc6fc7e
chg: move entity dataset name character error to ErrorCode enum
lindsay-stevens Dec 17, 2025
1ab9d88
chg: replace entity save_to name error with same ErrorCode.NAMES_008
lindsay-stevens Dec 17, 2025
c3beb3e
chg: replace select_from_file name error with same ErrorCode.NAMES_008
lindsay-stevens Dec 17, 2025
0069f41
chg: replace workbook_to_json name error with same ErrorCode.NAMES_008
lindsay-stevens Dec 17, 2025
ead8652
chg: replace element/form name error with same ErrorCode.NAMES_009
lindsay-stevens Dec 17, 2025
a06d685
chg: move entity name underscores error ErrorCode enum
lindsay-stevens Dec 17, 2025
06607da
chg: move entity name period error ErrorCode enum
lindsay-stevens Dec 17, 2025
83ca653
chg: move entity name save_to reserved words error ErrorCode enum
lindsay-stevens Dec 17, 2025
d42a43c
fix: missing f-string prefix on big-image error message
lindsay-stevens Dec 17, 2025
41ac57b
add: check the "name" setting as well as "form_name"
lindsay-stevens Dec 19, 2025
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
1 change: 0 additions & 1 deletion pyxform/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ class EntityColumns(StrEnum):
EXTERNAL_CHOICES_ITEMSET_REF_VALUE_GEOJSON = "id"

ROW_FORMAT_STRING: str = "[row : %s]"
XML_IDENTIFIER_ERROR_MESSAGE = "must begin with a letter, colon, or underscore. Other characters can include numbers, dashes, and periods."
_MSG_SUPPRESS_SPELLING = (
" If you do not mean to include a sheet, to suppress this message, "
"prefix the sheet name with an underscore. For example 'setting' "
Expand Down
122 changes: 38 additions & 84 deletions pyxform/entities/entities_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,11 @@

from pyxform import constants as const
from pyxform.elements import action
from pyxform.errors import Detail, PyXFormError
from pyxform.errors import ErrorCode, PyXFormError
from pyxform.parsing.expression import is_xml_tag
from pyxform.validators.pyxform.pyxform_reference import parse_pyxform_references

EC = const.EntityColumns
ENTITY001 = Detail(
name="Invalid entity repeat reference",
msg=(
"[row : 2] On the 'entities' sheet, the 'repeat' value '{value}' is invalid. "
"The 'repeat' column, if specified, must contain only a single reference variable "
"(like '${{q1}}'), and the reference variable must contain a valid name."
),
)
ENTITY002 = Detail(
name="Invalid entity repeat: target not found",
msg=(
"[row : 2] On the 'entities' sheet, the 'repeat' value '{value}' is invalid. "
"The entity repeat target was not found in the 'survey' sheet."
),
)
ENTITY003 = Detail(
name="Invalid entity repeat: target is not a repeat",
msg=(
"[row : 2] On the 'entities' sheet, the 'repeat' value '{value}' is invalid. "
"The entity repeat target is not a repeat."
),
)
ENTITY004 = Detail(
name="Invalid entity repeat: target is in a repeat",
msg=(
"[row : 2] On the 'entities' sheet, the 'repeat' value '{value}' is invalid. "
"The entity repeat target is inside a repeat."
),
)
ENTITY005 = Detail(
name="Invalid entity repeat save_to: question in nested repeat",
msg=(
"[row : {row}] On the 'survey' sheet, the 'save_to' value '{value}' is invalid. "
"The entity property populated with 'save_to' must not be inside of a nested "
"repeat within the entity repeat."
),
)
ENTITY006 = Detail(
name="Invalid entity repeat save_to: question not in entity repeat",
msg=(
"[row : {row}] On the 'survey' sheet, the 'save_to' value '{value}' is invalid. "
"The entity property populated with 'save_to' must be inside of the entity "
"repeat."
),
)
ENTITY007 = Detail(
name="Invalid entity repeat save_to: question in repeat but no entity repeat defined",
msg=(
"[row : {row}] On the 'survey' sheet, the 'save_to' value '{value}' is invalid. "
"The entity property populated with 'save_to' must be inside a repeat that is "
"declared in the 'repeat' column of the 'entities' sheet."
),
)


def get_entity_declaration(
Expand Down Expand Up @@ -232,20 +179,21 @@ def get_validated_dataset_name(entity):

if dataset.startswith(const.ENTITIES_RESERVED_PREFIX):
raise PyXFormError(
f"Invalid entity list name: '{dataset}' starts with reserved prefix {const.ENTITIES_RESERVED_PREFIX}."
ErrorCode.NAMES_010.value.format(
sheet=const.ENTITIES, row=2, column=EC.DATASET.value
)
)

if "." in dataset:
elif "." in dataset:
raise PyXFormError(
f"Invalid entity list name: '{dataset}'. Names may not include periods."
ErrorCode.NAMES_011.value.format(
sheet=const.ENTITIES, row=2, column=EC.DATASET.value
)
)

if not is_xml_tag(dataset):
if isinstance(dataset, bytes):
dataset = dataset.decode("utf-8")

elif not is_xml_tag(dataset):
raise PyXFormError(
f"Invalid entity list name: '{dataset}'. Names must begin with a letter, colon, or underscore. Other characters can include numbers or dashes."
ErrorCode.NAMES_008.value.format(
sheet=const.ENTITIES, row=2, column=EC.DATASET.value
)
)

return dataset
Expand All @@ -263,7 +211,7 @@ def get_validated_repeat_name(entity) -> str | None:
raise
else:
if not match or match[0].last_saved:
raise PyXFormError(ENTITY001.format(value=value))
raise PyXFormError(ErrorCode.ENTITY_001.value.format(value=value))
else:
return match[0].name

Expand Down Expand Up @@ -297,37 +245,43 @@ def validate_entity_saveto(
elif i["control_type"] == const.REPEAT:
# Error: saveto in nested repeat inside entity repeat.
if in_repeat:
raise PyXFormError(ENTITY005.format(row=row_number, value=save_to))
raise PyXFormError(
ErrorCode.ENTITY_005.value.format(row=row_number, value=save_to)
)
elif i["control_name"] == entity_repeat:
located = True
in_repeat = True

# Error: saveto not in entity repeat
if entity_repeat and not located:
raise PyXFormError(ENTITY006.format(row=row_number, value=save_to))
raise PyXFormError(
ErrorCode.ENTITY_006.value.format(row=row_number, value=save_to)
)

# Error: saveto in repeat but no entity repeat declared
if in_repeat and not entity_repeat:
raise PyXFormError(ENTITY007.format(row=row_number, value=save_to))

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

if save_to.lower() == const.NAME or save_to.lower() == const.LABEL:
raise PyXFormError(
f"{error_start} the entity property name '{save_to}' is reserved."
ErrorCode.ENTITY_007.value.format(row=row_number, value=save_to)
)

if save_to.startswith(const.ENTITIES_RESERVED_PREFIX):
# Error: naming rules
if save_to.lower() in {const.NAME, const.LABEL}:
raise PyXFormError(
f"{error_start} the entity property name '{save_to}' starts with reserved prefix {const.ENTITIES_RESERVED_PREFIX}."
ErrorCode.NAMES_011.value.format(
sheet=const.SURVEY, row=row_number, column=const.ENTITIES_SAVETO
)
)

if not is_xml_tag(save_to):
if isinstance(save_to, bytes):
save_to = save_to.decode("utf-8")

elif save_to.startswith(const.ENTITIES_RESERVED_PREFIX):
raise PyXFormError(
ErrorCode.NAMES_010.value.format(
sheet=const.SURVEY, row=row_number, column=const.ENTITIES_SAVETO
)
)
elif not is_xml_tag(save_to):
raise PyXFormError(
f"{error_start} '{save_to}'. Entity property names {const.XML_IDENTIFIER_ERROR_MESSAGE}"
ErrorCode.NAMES_008.value.format(
sheet=const.SURVEY, row=row_number, column=const.ENTITIES_SAVETO
)
)


Expand Down Expand Up @@ -370,7 +324,7 @@ def validate_entity_repeat_target(

# Error: repeat not found while processing survey sheet.
if not stack:
raise PyXFormError(ENTITY002.format(value=entity_repeat))
raise PyXFormError(ErrorCode.ENTITY_002.value.format(value=entity_repeat))

control_name = stack[-1]["control_name"]
control_type = stack[-1]["control_type"]
Expand All @@ -381,7 +335,7 @@ def validate_entity_repeat_target(

# Error: target is not a repeat.
if control_type and control_type != const.REPEAT:
raise PyXFormError(ENTITY003.format(value=entity_repeat))
raise PyXFormError(ErrorCode.ENTITY_003.value.format(value=entity_repeat))

# Error: repeat is in nested repeat.
located = False
Expand All @@ -390,7 +344,7 @@ def validate_entity_repeat_target(
break
elif i["control_type"] == const.REPEAT:
if located:
raise PyXFormError(ENTITY004.format(value=entity_repeat))
raise PyXFormError(ErrorCode.ENTITY_004.value.format(value=entity_repeat))
elif i["control_name"] == entity_repeat:
located = True

Expand Down
Loading