-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Further isolate entities implementation
- Loading branch information
1 parent
935685c
commit f6cd4a8
Showing
8 changed files
with
150 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
from typing import Dict, List | ||
|
||
from pyxform import constants | ||
from pyxform.errors import PyXFormError | ||
from pyxform.xlsparseutils import find_sheet_misspellings, is_valid_xml_tag | ||
|
||
|
||
def get_entity_declaration(workbook_dict: Dict, warnings: List) -> Dict: | ||
entities_sheet = workbook_dict.get(constants.ENTITIES, []) | ||
|
||
if len(entities_sheet) == 0: | ||
similar = find_sheet_misspellings( | ||
key=constants.ENTITIES, keys=workbook_dict.keys() | ||
) | ||
if similar is not None: | ||
warnings.append(similar + constants._MSG_SUPPRESS_SPELLING) | ||
return {} | ||
elif len(entities_sheet) > 1: | ||
raise PyXFormError( | ||
"This version of pyxform only supports declaring a single entity per form. Please make sure your entities sheet only declares one entity." | ||
) | ||
|
||
entity = entities_sheet[0] | ||
dataset = entity["dataset"] | ||
|
||
if dataset.startswith(constants.ENTITIES_RESERVED_PREFIX): | ||
raise PyXFormError( | ||
f"Invalid dataset name: '{dataset}' starts with reserved prefix {constants.ENTITIES_RESERVED_PREFIX}." | ||
) | ||
|
||
if not is_valid_xml_tag(dataset): | ||
if isinstance(dataset, bytes): | ||
dataset = dataset.encode("utf-8") | ||
|
||
raise PyXFormError( | ||
f"Invalid dataset name: '{dataset}'. Dataset names {constants.XML_IDENTIFIER_ERROR_MESSAGE}" | ||
) | ||
|
||
if not ("label" in entity): | ||
raise PyXFormError("The entities sheet is missing the required label column.") | ||
|
||
creation_condition = entity["create_if"] if "create_if" in entity else "1" | ||
|
||
return { | ||
"name": "entity", | ||
"type": "entity", | ||
"parameters": { | ||
"dataset": dataset, | ||
"create": creation_condition, | ||
"label": entity["label"], | ||
}, | ||
} | ||
|
||
|
||
def validate_entity_saveto(row: Dict, row_number: int, entity_declaration: Dict): | ||
save_to = row.get("bind", {}).get("entities:saveto", "") | ||
if not save_to: | ||
return | ||
|
||
if len(entity_declaration) == 0: | ||
raise PyXFormError( | ||
"To save entity properties using the save_to column, you must add an entities sheet and declare an entity." | ||
) | ||
|
||
if constants.GROUP in row.get(constants.TYPE) or constants.REPEAT in row.get( | ||
constants.TYPE | ||
): | ||
raise PyXFormError( | ||
f"{constants.ROW_FORMAT_STRING % row_number} Groups and repeats can't be saved as entity properties." | ||
) | ||
|
||
error_start = f"{constants.ROW_FORMAT_STRING % row_number} Invalid save_to name:" | ||
|
||
if save_to == "name" or save_to == "label": | ||
raise PyXFormError( | ||
f"{error_start} the entity property name '{save_to}' is reserved." | ||
) | ||
|
||
if save_to.startswith(constants.ENTITIES_RESERVED_PREFIX): | ||
raise PyXFormError( | ||
f"{error_start} the entity property name '{save_to}' starts with reserved prefix {constants.ENTITIES_RESERVED_PREFIX}." | ||
) | ||
|
||
if not is_valid_xml_tag(save_to): | ||
if isinstance(save_to, bytes): | ||
save_to = save_to.encode("utf-8") | ||
|
||
raise PyXFormError( | ||
f"{error_start} '{save_to}'. Entity property names {constants.XML_IDENTIFIER_ERROR_MESSAGE}" | ||
) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import re | ||
from pyxform import constants | ||
from typing import KeysView, Optional | ||
|
||
from pyxform.utils import levenshtein_distance | ||
|
||
# http://www.w3.org/TR/REC-xml/ | ||
TAG_START_CHAR = r"[a-zA-Z:_]" | ||
TAG_CHAR = r"[a-zA-Z:_0-9\-.]" | ||
XFORM_TAG_REGEXP = "%(start)s%(char)s*" % {"start": TAG_START_CHAR, "char": TAG_CHAR} | ||
|
||
|
||
def find_sheet_misspellings(key: str, keys: "KeysView") -> "Optional[str]": | ||
""" | ||
Find possible sheet name misspellings to warn the user about. | ||
It's possible that this will warn about sheet names for sheets that have | ||
auxilliary metadata that is not meant for processing by pyxform. For | ||
example the "osm" sheet name may be similar to many other initialisms. | ||
:param key: The sheet name to look for. | ||
:param keys: The workbook sheet names. | ||
""" | ||
candidates = tuple( | ||
_k # thanks to black | ||
for _k in keys | ||
if 2 >= levenshtein_distance(_k.lower(), key) | ||
and _k not in constants.SUPPORTED_SHEET_NAMES | ||
and not _k.startswith("_") | ||
) | ||
if 0 < len(candidates): | ||
msg = ( | ||
"When looking for a sheet named '{k}', the following sheets with " | ||
"similar names were found: {c}." | ||
).format(k=key, c=str(", ".join(("'{}'".format(c) for c in candidates)))) | ||
return msg | ||
else: | ||
return None | ||
|
||
|
||
def is_valid_xml_tag(tag): | ||
""" | ||
Use a regex to see if there are any invalid characters (i.e. spaces). | ||
""" | ||
return re.search(r"^" + XFORM_TAG_REGEXP + r"$", tag) |