diff --git a/docs/assets/images/img-excel2xml-module-docstring.png b/docs/assets/images/img-excel2xml-module-docstring.png
new file mode 100644
index 000000000..fd37d2810
Binary files /dev/null and b/docs/assets/images/img-excel2xml-module-docstring.png differ
diff --git a/docs/assets/templates/excel2xml_sample_script.py b/docs/assets/templates/excel2xml_sample_script.py
index 25429c600..6ea630929 100644
--- a/docs/assets/templates/excel2xml_sample_script.py
+++ b/docs/assets/templates/excel2xml_sample_script.py
@@ -47,9 +47,9 @@
# and if it's not there, look in "category_dict_fallback"
category_values = [category_dict.get(x.strip(), category_dict_fallback[x.strip()]) for x in
row["Category"].split(",")]
- resource.append(excel2xml.make_list_prop("category", ":hasCategory", values=category_values))
+ resource.append(excel2xml.make_list_prop("category", ":hasCategory", category_values))
if excel2xml.check_notna(row["Complete?"]):
- resource.append(excel2xml.make_boolean_prop(name=":isComplete", value=row["Complete?"]))
+ resource.append(excel2xml.make_boolean_prop(":isComplete", row["Complete?"]))
if excel2xml.check_notna(row["Color"]):
resource.append(excel2xml.make_color_prop(":colorprop", row["Color"]))
if pd.notna(row["Date discovered"]):
@@ -93,7 +93,7 @@
link = excel2xml.make_link("Link between Resource 0 and 1", "link_res_0_res_1")
link.append(excel2xml.make_text_prop("hasComment", "This is a comment"))
-link.append(excel2xml.make_resptr_prop("hasLinkTo", values=["res_0", "res_1"]))
+link.append(excel2xml.make_resptr_prop("hasLinkTo", ["res_0", "res_1"]))
root.append(link)
# write file
diff --git a/docs/dsp-tools-excel2xml.md b/docs/dsp-tools-excel2xml.md
index 5cc21d200..7b5527a00 100644
--- a/docs/dsp-tools-excel2xml.md
+++ b/docs/dsp-tools-excel2xml.md
@@ -5,30 +5,72 @@ dsp-tools assists you in converting a data source in CSV/XLS(X) format to an XML
transformation from Excel/CSV to XML:
- The CLI command `dsp-tools excel2xml` creates an XML file from an Excel/CSV file which is already structured
- according to the DSP specifications. This is mostly used for DaSCH-interal data migration. The CLI command is
- documented [here](dsp-tools-excel.md#cli-command-excel2xml).
+ according to the DSP specifications. This is mostly used for DaSCH-interal data migration. **The CLI command is
+ documented [here](dsp-tools-excel.md#cli-command-excel2xml).**
- The module `excel2xml` can be imported into a custom Python script that transforms any tabular data into an XML. This
- use case is more frequent, because data from research projects have a variety of formats/structures. **This document
- only treats the `excel2xml` module.**
+ use case is more frequent, because data from research projects have a variety of formats/structures. **The
+ `excel2xml` module is documented on this page.**
+
+**In the following, an example is given how to use the module `excel2xml`:**
-## How to use the module excel2xml
-At the end of this document, you find a sample Python script. In the following, it is explained how to use it.
+Save the following files into a directory, and run the Python script:
+ - sample data: [excel2xml_sample_data.csv](./assets/templates/excel2xml_sample_data.csv)
+ - sample ontology: [excel2xml_sample_onto.json](./assets/templates/excel2xml_sample_onto.json)
+ - sample script: [excel2xml_sample_script.py](./assets/templates/excel2xml_sample_script.py)
-### General preparation
-Insert your ontology name, project shortcode, and the path to your data source. If necessary, activate one of the lines
-that are commented out.
-Then, the `root` element is created, which represents the `` tag of the XML document. As first children of
-``, some standard permissions are added. At the end, please carefully check the permissions of the finished XML
-file if they meet your requirements, and adapt them if necessary.
-The standard permission of a resource is "res-default", and of a property "prop-default". If you don't specify it
-otherwise, all resources and properties get these permissions. With excel2xml, it is not possible to create resources/
-properties that don't have permissions, because they would be invisible for all users except project admins and system
-admins. Read more about permissions [here](./dsp-tools-xmlupload.md#how-to-use-the-permissions-attribute-in-resourcesproperties).
+This is the simplified pattern how the Python script works:
+```
+1 main_df = pd.read_csv("excel2xml_sample_data.csv", dtype="str", sep=",")
+2 root = excel2xml.make_root(...)
+3 root = excel2xml.append_permissions(root)
+4 # if necessary: create list mappings, according to explanation below
+5 for index, row in main_df.iterrows():
+6 resource = excel2xml.make_resource(...)
+7 resource.append(excel2xml.make_text_prop(...))
+8 root.append(resource)
+9 excel2xml.write_xml(root, "data.xml")
+```
+```
+1 read in your data source with the pandas library (https://pandas.pydata.org/)
+2 create the root element ``
+3 append the permissions
+4 if necessary: create list mappings (see below)
+5 iterate through the rows of your data source:
+6 create the `` tag
+7 append properties to it
+8 append the resource to the root tag ``
+9 save the finished XML file
+```
-### Create list mappings
+
+These steps are now explained in-depth:
+
+
+## 1. Read in your data source
+In the first paragraph of the sample script, insert your ontology name, project shortcode, and the path to your data
+source. If necessary, activate one of the lines that are commented out.
+
+
+## 2. Create root element ``
+Then, the root element is created, which represents the `` tag of the XML document.
+
+
+## 3. Append the permissions
+As first children of ``, some standard permissions are added. At the end, please carefully check the permissions
+of the finished XML file to ensure that they meet your requirements, and adapt them if necessary.
+
+The standard permission of a resource is `res-default`, and of a property `prop-default`. If you don't specify it
+otherwise, all resources and properties get these permissions.
+
+With `excel2xml`, it is not possible to create resources/properties that don't have permissions, because they would be
+invisible for all users except project admins and system admins. [Read more about permissions
+here](./dsp-tools-xmlupload.md#how-to-use-the-permissions-attribute-in-resourcesproperties).
+
+
+## 4. Create list mappings
Let's assume that your data source has a column containing list values named after the "label" of the JSON project list,
instead of the "name" which is needed for the `dsp-tools xmlupload`. You need a way to get the names from the labels.
If your data source uses the labels correctly, this is an easy task: The method `create_json_list_mapping()` creates a
@@ -39,38 +81,117 @@ correct JSON project node name. This happens based on string similarity. Please
no false matches!
-### Create all resources
-With the help of the [Python pandas library](https://pandas.pydata.org/), you can then iterate through the rows of your
-Excel/CSV, and create resources and properties. Some examples of useful helper methods are:
+## 5. Iterate through the rows of your data source
+With the help of Pandas, you can then iterate through the rows of your Excel/CSV, and create resources and properties.
+
+
+### 6. Create the `` tag
+There are four kind of resources that can be created:
+
+| super | tag | method |
+|--------------|----------------|---------------------|
+| `Resource` | `` | `make_resource()` |
+| `Annotation` | `` | `make_annotation()` |
+| `Region` | `` | `make_region()` |
+| `LinkObj` | `` | `make_link()` |
+
+`` is the most frequent of them. The other three are [explained
+here](./dsp-tools-xmlupload.md#dsp-base-resources--base-properties-to-be-used-directly-in-the-xml-file).
+
+Special care is needed when the ID of a resource is created. Every resource must have an ID that is unique in the file,
+and it must meet the constraints of xsd:ID. You can simply achieve this if you use the method `make_xsd_id_compatible()`.
+
+
+### 7. Append the properties
+For every property, there is a helper function that explains itself when you hover over it. So you don't need to worry
+any more how to construct a certain XML value for a certain property.
+
+Here's how the Docstrings assist you:
+
+ - method signature: names of the parameters and accepted types
+ - short explanation how the method behaves
+ - usage examples
+ - link to the dsp-tools documentation of this property
+ - a short description for every parameter
+ - short description of the returned object.
+ - Note: `etree._Element` is a type annotation of an underlying library. You don't have to care about it, as long as
+ you proceed as described (append the returned object to the parent resource).
+
+![docstring example](./assets/images/img-excel2xml-module-docstring.png)
+
+
+#### Fine-tuning with `PropertyElement`
+There are two possibilities how to create a property: The value can be passed as it is, or as `PropertyElement`. If it
+is passed as it is, the `permissions` are assumed to be `prop-default`, texts are assumed to be encoded as `utf8`, and
+the value won't have a comment:
+```
+make_text_prop(":testproperty", "first text")
+```
+```
+
+ first text
+
+```
+
+If you want to change these defaults, you have to use a `PropertyElement` instead:
+```
+make_text_prop(
+ ":testproperty",
+ PropertyElement(
+ value="first text",
+ permissions="prop-restricted",
+ encoding="xml",
+ comment="some comment"
+ )
+)
+```
+```
+
+ first text
+
+```
+
+
+#### Supported boolean formats
+For `make_boolean_prop(cell)`, the following formats are supported:
+ - true: True, "true", "True", "1", 1, "yes", "Yes"
+ - false: False, "false", "False", "0", 0, "no", "No"
-#### Create an ID for a resource
-The method `make_xsd_id_compatible(string)` makes a string compatible with the constraints of xsd:ID, so that it can be
-used as ID of a resource.
+N/A-like values will raise an Error. So if your cell is empty, this method will not count it as false, but will raise an
+Error. If you want N/A-like values to be counted as false, you may use a construct like this:
+```python
+if excel2xml.check_notna(cell):
+ # the cell contains usable content
+ excel2xml.make_boolean_prop(":hasBoolean", cell)
+else:
+ # the cell is empty: you can decide to count this as "False"
+ excel2xml.make_boolean_prop(":hasBoolean", False)
+```
-#### Create a property
-For every property, there is a helper function that explains itself when you hover over it. It also has a link to
-the dsp-tools documentation of this property. So you don't need to worry how to construct a certain XML value for a
-certain property.
-For `make_boolean_prop(cell)`, the following formats are supported:
+### 8. Append the resource to root
+At the end of the for-loop, it is important not to forget to append the finished resource to the root.
- - true: True, "true", "True", "1", 1, "yes", "Yes"
- - false: False, "false", "False", "0", 0, "no", "No"
+
+## 9. Save the file
+At the very end, save the file under a name that you can choose yourself.
-#### Check if a cell contains a usable value
+## Other helper methods
+### Check if a cell contains a usable value
The method `check_notna(cell)` checks a value if it is usable in the context of data archiving. A value is considered
usable if it is
- a number (integer or float, but not numpy.nan)
- a boolean
- - a string with at least one Unicode letter, underscore, or number, but not "None", "", "N/A", or "-"
+ - a string with at least one Unicode letter (matching the regex `\p{L}`), underscore, ?, !, or number, but not "None",
+ "", "N/A", or "-"
- a PropertyElement whose "value" fulfills the above criteria
-#### Calendar date parsing
+### Calendar date parsing
The method `find_date_in_string(string)` tries to find a calendar date in a string. If successful, it
returns the DSP-formatted date string.
@@ -99,12 +220,3 @@ Currently supported date formats:
| 1849/1850 | GREGORIAN:CE:1849:CE:1850 |
| 1849/50 | GREGORIAN:CE:1849:CE:1850 |
| 1845-50 | GREGORIAN:CE:1845:CE:1850 |
-
-
-## Complete example
-Save the following files into a directory, and run the Python script. The features discussed in this document are
-contained therein.
-
- - sample data: [excel2xml_sample_data.csv](assets/templates/excel2xml_sample_data.csv)
- - sample ontology: [excel2xml_sample_onto.json](assets/templates/excel2xml_sample_onto.json)
- - sample script: [excel2xml_sample_script.py](assets/templates/excel2xml_sample_script.py)
diff --git a/knora/dsplib/models/propertyelement.py b/knora/dsplib/models/propertyelement.py
index eb3291a42..ffa2e981d 100644
--- a/knora/dsplib/models/propertyelement.py
+++ b/knora/dsplib/models/propertyelement.py
@@ -1,6 +1,4 @@
from typing import Union, Optional
-import pandas as pd
-import regex
from dataclasses import dataclass
from knora.dsplib.models.helpers import BaseError
@@ -10,13 +8,13 @@ class PropertyElement:
"""
A PropertyElement object carries more information about a property value than the value itself.
The "value" is the value that could be passed to a method as plain string/int/float/bool. Use a PropertyElement
- instead to define more precisely what attributes your tag (for example) will have.
+ instead to define more precisely what attributes your value tag (e.g. , , ...) will have.
Args:
- value: This is the content that will be written between the tags (for example)
+ value: This is the content that will be written into the value tag (e.g. , , ...)
permissions: This is the permissions that your tag (for example) will have
comment: This is the comment that your tag (for example) will have
- encoding: For tags only. Can be "xml" or "utf8".
+ encoding: For tags only. If provided, it must be "xml" or "utf8".
Examples:
See the difference between the first and the second example:
@@ -40,15 +38,5 @@ class PropertyElement:
encoding: Optional[str] = None
def __post_init__(self) -> None:
- if not any([
- isinstance(self.value, int),
- isinstance(self.value, float) and pd.notna(self.value), # necessary because isinstance(np.nan, float)
- isinstance(self.value, bool),
- isinstance(self.value, str) and all([
- regex.search(r"\p{L}|\d|_", self.value, flags=regex.UNICODE),
- not bool(regex.search(r"^(none||-|n/a)$", self.value, flags=regex.IGNORECASE))
- ])
- ]):
- raise BaseError(f"'{self.value}' is not a valid value for a PropertyElement")
if self.encoding not in ["utf8", "xml", None]:
raise BaseError(f"'{self.encoding}' is not a valid encoding for a PropertyElement")
diff --git a/knora/dsplib/utils/shared.py b/knora/dsplib/utils/shared.py
index a99fd86ce..7c7f74811 100644
--- a/knora/dsplib/utils/shared.py
+++ b/knora/dsplib/utils/shared.py
@@ -174,7 +174,8 @@ def check_notna(value: Optional[Any]) -> bool:
Check a value if it is usable in the context of data archiving. A value is considered usable if it is
- a number (integer or float, but not np.nan)
- a boolean
- - a string with at least one Unicode letter, underscore, or number, but not "None", "", "N/A", or "-"
+ - a string with at least one Unicode letter (matching the regex ``\\p{L}``), underscore, !, ?, or number, but not
+ "None", "", "N/A", or "-"
- a PropertyElement whose "value" fulfills the above criteria
Args:
@@ -195,7 +196,7 @@ def check_notna(value: Optional[Any]) -> bool:
return True
elif isinstance(value, str):
return all([
- regex.search(r"\p{L}|\d|_", value, flags=regex.UNICODE),
+ regex.search(r"[\p{L}\d_!?]", value, flags=regex.UNICODE),
not bool(regex.search(r"^(none||-|n/a)$", value, flags=regex.IGNORECASE))
])
else:
diff --git a/knora/excel2xml.py b/knora/excel2xml.py
index af6e79a0e..aa009174a 100644
--- a/knora/excel2xml.py
+++ b/knora/excel2xml.py
@@ -18,37 +18,32 @@
from knora.dsplib.models.propertyelement import PropertyElement
from knora.dsplib.utils.shared import simplify_name, check_notna
-##############################
-# global variables and classes
-##############################
+
xml_namespace_map = {
None: "https://dasch.swiss/schema",
"xsi": "http://www.w3.org/2001/XMLSchema-instance"
}
-###########
-# functions
-###########
def make_xsd_id_compatible(string: str) -> str:
"""
- Make a string compatible with the constraints of xsd:ID as defined in http://www.datypic.com/sc/xsd/t-xsd_ID.html.
- An xsd:ID cannot contain special characters, and it must be unique in the document.
+ Make a string compatible with the constraints of xsd:ID, so that it can be used as "id" attribute of a
+ tag. An xsd:ID must not contain special characters, and it must be unique in the document.
This method replaces the illegal characters by "_" and appends a random number to the string to make it unique.
- The string must contain at least one word-character (regex [A-Za-z0-9_]), but must not be "None", "", "N/A", or
- "-". In such cases, a BaseError is thrown.
+ The string must contain at least one Unicode letter (matching the regex ``\\p{L}``), underscore, !, ?, or number,
+ but must not be "None", "", "N/A", or "-". Otherwise, a BaseError will be raised.
Args:
- string: string which to make the xsd:ID from
+ string: input string
Returns:
- an `xsd:ID` based on string
+ an xsd:ID based on the input string
"""
if not isinstance(string, str) or not check_notna(string):
- raise BaseError(f"The string {string} cannot be made an xsd:ID")
+ raise BaseError(f"The input '{string}' cannot be transformed to an xsd:ID")
# if start of string is neither letter nor underscore, add an underscore
res = re.sub(r"^(?=[^A-Za-z_])", "_", string)
@@ -69,7 +64,8 @@ def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str
DSP-formatted string. Returns None if no date was found.
Notes:
- - Assumes Christian era (no BC dates) and Gregorian calendar.
+ - All dates are interpreted in the Christian era and the Gregorian calendar. There is no support for BC dates or
+ non-Gregorian calendars.
- The years 0000-2999 are supported, in 4-digit form.
- Dates written with slashes are always interpreted in a European manner: 5/11/2021 is the 5th of November.
@@ -165,8 +161,6 @@ def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str
startdate = datetime.date(year, month, day)
enddate = startdate
except ValueError:
- warnings.warn(f"Date parsing error in resource {calling_resource}: '{iso_date.group(0)}' is not a valid "
- f"date", stacklevel=2)
return None
elif eur_date_range:
@@ -182,8 +176,6 @@ def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str
if enddate < startdate:
raise ValueError
except ValueError:
- warnings.warn(f"Date parsing error in resource {calling_resource}: '{eur_date_range.group(0)}' is not a "
- f"valid date", stacklevel=2)
return None
elif eur_date:
@@ -194,8 +186,6 @@ def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str
startdate = datetime.date(startyear, startmonth, startday)
enddate = startdate
except ValueError:
- warnings.warn(f"Date parsing error in resource {calling_resource}: '{eur_date.group(0)}' is not a valid "
- f"date", stacklevel=2)
return None
elif monthname_date:
@@ -206,8 +196,6 @@ def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str
startdate = datetime.date(year, month, day)
enddate = startdate
except ValueError:
- warnings.warn(f"Date parsing error in resource {calling_resource}: '{monthname_date.group(0)}' is not a "
- f"valid date", stacklevel=2)
return None
elif year_range:
@@ -229,72 +217,28 @@ def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str
return None
-def _check_and_prepare_values(
- value: Optional[Union[PropertyElement, str, int, float, bool]],
- values: Optional[Iterable[Union[PropertyElement, str, int, float, bool]]],
- name: str,
- calling_resource: str = ""
+def prepare_value(
+ value: Union[PropertyElement, str, int, float, bool, Iterable[Union[PropertyElement, str, int, float, bool]]]
) -> list[PropertyElement]:
"""
- There is a variety of possibilities how to call a make_*_prop() method. Before such a method can do its job, the
- parameters need to be checked and prepared, which is done by this helper method. The parameters "value" and "values"
- are passed to it as they were received. This method will then perform the following checks, and throw a BaseError in
- case of failure:
- - check that exactly one of them contains data, but not both.
- - check that the values are usable, and not N/A
-
- Then, all values are transformed to PropertyElements and returned as a list. In case of a single "value", the
- resulting list contains the PropertyElement of this value.
+ This method transforms the parameter "value" from a make_*_prop() method into a list of PropertyElements. "value" is
+ passed on to this method as it was received.
Args:
value: "value" as received from the caller
- values: "values" as received from the caller
- name: name of the property (for better error messages)
- calling_resource: name of the resource (for better error messages)
Returns:
a list of PropertyElements
"""
+ # make sure that "value" is list-like
+ if not isinstance(value, Iterable) or isinstance(value, str):
+ value = [value]
- # reduce 'value' to None if it is not usable
- if not check_notna(value):
- value = None
-
- # reduce 'values' to None if it is not usable
- if values and not any([check_notna(val) for val in values]):
- values = None
-
- # assert that either "value" or "values" is usable, but not both at the same time
- if not value and not values:
- raise BaseError(f"ERROR in resource '{calling_resource}', property '{name}': 'value' and 'values' cannot both "
- f"be empty")
- if value and values:
- raise BaseError(f"ERROR in resource '{calling_resource}', property '{name}': You cannot provide a 'value' and "
- f"a 'values' at the same time!")
-
- # construct the resulting list
- result: list[PropertyElement] = list()
-
- if value:
- # make a PropertyElement out of it, if necessary
- if isinstance(value, PropertyElement):
- result.append(value)
- else:
- result.append(PropertyElement(value))
- elif values:
- # if "values" contains unusable elements, remove them
- multiple_values = [val for val in values if check_notna(val)]
- # make a PropertyElement out of them, if necessary
- for elem in multiple_values:
- if isinstance(elem, PropertyElement):
- result.append(elem)
- else:
- result.append(PropertyElement(elem))
-
- return result
+ # make a PropertyElement out of its elements, if necessary.
+ return [x if isinstance(x, PropertyElement) else PropertyElement(x) for x in value]
-def make_root(shortcode: str, default_ontology: str) -> etree.Element:
+def make_root(shortcode: str, default_ontology: str) -> etree._Element:
"""
Start building your XML document by creating the root element .
@@ -326,7 +270,7 @@ def make_root(shortcode: str, default_ontology: str) -> etree.Element:
return root
-def append_permissions(root_element: etree.Element) -> etree.Element:
+def append_permissions(root_element: etree.Element) -> etree._Element:
"""
After having created a root element, call this method to append the four permissions "res-default",
"res-restricted", "prop-default", and "prop-restricted" to it. These four permissions are a good basis to
@@ -388,7 +332,7 @@ def make_resource(
permissions: str = "res-default",
ark: Optional[str] = None,
iri: Optional[str] = None
-) -> etree.Element:
+) -> etree._Element:
"""
Creates an empty resource element, with the attributes as specified by the arguments
@@ -433,9 +377,10 @@ def make_bitstream_prop(
path: str,
permissions: str = "prop-default",
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
- Creates a bitstream element that points to path.
+ Creates a bitstream element that points to "path". If "path" doesn't point to a valid file, a warning will be
+ printed to the console, but the script will continue.
Args:
path: path to a valid file that will be uploaded
@@ -443,7 +388,7 @@ def make_bitstream_prop(
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> resource = make_resource(...)
@@ -454,7 +399,8 @@ def make_bitstream_prop(
"""
if not os.path.isfile(path):
- warnings.warn(f"The following is not a valid path: {path} (resource '{calling_resource}')",
+ warnings.warn(f"Failed validation in bitstream tag of resource '{calling_resource}': The following path "
+ f"doesn't point to a file: {path}",
stacklevel=2)
prop_ = etree.Element("{%s}bitstream" % (xml_namespace_map[None]), permissions=permissions,
nsmap=xml_namespace_map)
@@ -470,29 +416,32 @@ def _format_bool(unformatted: Union[bool, str, int], name: str, calling_resource
elif unformatted in (True, "true", "1", 1, "yes"):
return "true"
else:
- raise BaseError(f"Invalid boolean format for prop '{name}' in resource '{calling_resource}': '{unformatted}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{unformatted}' is not a valid boolean.")
def make_boolean_prop(
name: str,
value: Union[PropertyElement, str, int, bool],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from a boolean value. The value can be provided directly or inside a PropertyElement. The
following formats are supported:
- true: (True, "true", "True", "1", 1, "yes", "Yes")
- false: (False, "false", "False", "0", 0, "no", "No")
- Unless provided as PropertyElement, the permission for every value is "prop-default".
+ If the value is not a valid boolean, a BaseError will be raised.
+
+ Unless provided as PropertyElement, the permissions of the value default to "prop-default".
Args:
name: the name of this property as defined in the onto
- value: a str/bool/int itself or inside a PropertyElement
+ value: a boolean value as str/bool/int, or as str/bool/int inside a PropertyElement
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_boolean_prop(":testproperty", "no")
@@ -513,7 +462,8 @@ def make_boolean_prop(
elif isinstance(value, str) or isinstance(value, bool) or isinstance(value, int):
value_new = PropertyElement(_format_bool(value, name, calling_resource))
else:
- raise BaseError(f"Invalid boolean format for prop '{name}' in resource '{calling_resource}': '{value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{value}' is not a valid boolean.")
# make xml structure of the value
prop_ = etree.Element(
@@ -537,25 +487,22 @@ def make_boolean_prop(
def make_color_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more colors. The color(s) can be provided as string or as PropertyElement with a
- string inside. If provided as string, the permission for every value is "prop-default".
+ string inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the value is not a valid color, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more DSP color(s), as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_color_prop(":testproperty", "#00ff66")
@@ -566,7 +513,7 @@ def make_color_prop(
#00ff66
- >>> make_color_prop(":testproperty", values=["#00ff66", "#000000"])
+ >>> make_color_prop(":testproperty", ["#00ff66", "#000000"])
#00ff66#000000
@@ -576,25 +523,21 @@ def make_color_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.search(r"^#[0-9a-f]{6}$", str(val.value).strip(), flags=re.IGNORECASE):
- raise BaseError(f"Invalid color format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid color.")
- # make xml structure of the value
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}color-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -611,24 +554,22 @@ def make_color_prop(
def make_date_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more dates/date ranges. The date(s) can be provided as string or as PropertyElement
- with a string inside. If provided as string, the permission for every value is "prop-default".
+ with a string inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use ``values``.
+ If the value is not a valid DSP date, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more DSP dates, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_date_prop(":testproperty", "GREGORIAN:CE:2014-01-31")
@@ -641,7 +582,7 @@ def make_date_prop(
GREGORIAN:CE:2014-01-31
- >>> make_date_prop(":testproperty", values=["GREGORIAN:CE:1930-09-02:CE:1930-09-03", "GREGORIAN:CE:1930-09-02:CE:1930-09-03"])
+ >>> make_date_prop(":testproperty", ["GREGORIAN:CE:1930-09-02:CE:1930-09-03", "GREGORIAN:CE:1930-09-02:CE:1930-09-03"])
GREGORIAN:CE:1930-09-02:CE:1930-09-03
@@ -655,26 +596,22 @@ def make_date_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.search(r"^(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})(-\d{1,2})?(-\d{1,2})?"
r"((:CE|:BCE)?(:\d{4})(-\d{1,2})?(-\d{1,2})?)?$", str(val.value).strip()):
- raise BaseError(f"Invalid date format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid DSP date.")
- # make xml structure of the value
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}date-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -691,26 +628,23 @@ def make_date_prop(
def make_decimal_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more decimal numbers. The decimal(s) can be provided as string, float, or as
- PropertyElement with a string/float inside. If provided as string/float, the permission for every value is
+ PropertyElement with a string/float inside. If provided as string/float, the permissions default to
"prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the value is not a valid decimal number, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/float/PropertyElement
- values: an iterable of distinct strings/PropertyElements
+ value: one or more decimal numbers, as string/float/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_decimal_prop(":testproperty", "3.14159")
@@ -721,7 +655,7 @@ def make_decimal_prop(
3.14159
- >>> make_decimal_prop(":testproperty", values=["3.14159", "2.718"])
+ >>> make_decimal_prop(":testproperty", ["3.14159", "2.718"])
3.141592.718
@@ -731,25 +665,21 @@ def make_decimal_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.search(r"^\d+\.\d+$", str(val.value).strip()):
- raise BaseError(f"Invalid decimal format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid decimal number.")
- # make xml structure of the value
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}decimal-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -766,25 +696,22 @@ def make_decimal_prop(
def make_geometry_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more areas of an image. The area(s) can be provided as JSON-string or as
- PropertyElement with the JSON-string inside. If provided as string, the permission for every value is "prop-default".
+ PropertyElement with the JSON-string inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the value is not a valid JSON geometry object, a BaseError is raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more JSON geometry objects, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_geometry_prop(":testproperty", json_string)
@@ -795,7 +722,7 @@ def make_geometry_prop(
{JSON}
- >>> make_geometry_prop(":testproperty", values=[json_string1, json_string2])
+ >>> make_geometry_prop(":testproperty", [json_string1, json_string2])
{JSON}{JSON}
@@ -805,30 +732,25 @@ def make_geometry_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
try:
value_as_dict = json.loads(val.value)
- assert value_as_dict["type"] in ["rectangle", "circle"]
+ assert value_as_dict["type"] in ["rectangle", "circle", "polygon"]
assert isinstance(value_as_dict["points"], list)
except (json.JSONDecodeError, TypeError, IndexError, KeyError, AssertionError):
- raise BaseError(f"Invalid geometry format for prop '{name}' in resource '{calling_resource}': "
- f"'{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid JSON geometry object.")
- # make xml structure of the value
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}geometry-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -844,26 +766,23 @@ def make_geometry_prop(
def make_geoname_prop(
name: str,
- value: Optional[Union[PropertyElement, str, int]] = None,
- values: Optional[Iterable[Union[PropertyElement, str, int]]] = None,
+ value: Union[PropertyElement, str, int, Iterable[Union[PropertyElement, str, int]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more geonames.org IDs. The ID(s) can be provided as string, integer, or as
- PropertyElement with a string/integer inside. If provided as string/integer, the permission for every value is
+ PropertyElement with a string/integer inside. If provided as string/integer, the permissions default to
"prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the value is not a valid geonames.org identifier, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/int/PropertyElement
- values: an iterable of (usually distinct) strings/ints/PropertyElements
+ value: one or more geonames.org IDs, as string/int/PropertyElement, or as iterable of strings/ints/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_geoname_prop(":testproperty", "2761369")
@@ -874,7 +793,7 @@ def make_geoname_prop(
2761369
- >>> make_geoname_prop(":testproperty", values=["2761369", "1010101"])
+ >>> make_geoname_prop(":testproperty", ["2761369", "1010101"])
27613691010101
@@ -884,24 +803,21 @@ def make_geoname_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.search(r"^[0-9]+$", str(val.value)):
- raise BaseError(f"Invalid geoname format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a geonames.org identifier.")
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}geoname-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -918,26 +834,23 @@ def make_geoname_prop(
def make_integer_prop(
name: str,
- value: Optional[Union[PropertyElement, str, int]] = None,
- values: Optional[Iterable[Union[PropertyElement, str, int]]] = None,
+ value: Union[PropertyElement, str, int, Iterable[Union[PropertyElement, str, int]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more integers. The integers can be provided as string, integer, or as
- PropertyElement with a string/integer inside. If provided as string/integer, the permission for every value is
+ PropertyElement with a string/integer inside. If provided as string/integer, the permissions default to
"prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the value is not a valid integer, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/int/PropertyElement
- values: an iterable of (usually distinct) strings/ints/PropertyElements
+ value: one or more integers, as string/int/PropertyElement, or as iterable of strings/ints/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_integer_prop(":testproperty", "2761369")
@@ -948,7 +861,7 @@ def make_integer_prop(
2761369
- >>> make_integer_prop(":testproperty", values=["2761369", "1010101"])
+ >>> make_integer_prop(":testproperty", ["2761369", "1010101"])
27613691010101
@@ -958,24 +871,21 @@ def make_integer_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.search(r"^\d+$", str(val.value).strip()):
- raise BaseError(f"Invalid integer format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid integer.")
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}integer-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -992,25 +902,22 @@ def make_integer_prop(
def make_interval_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
- Make a from one or more intervals. The interval(s) can be provided as string or as PropertyElement
- with a string inside. If provided as string, the permission for every value is "prop-default".
+ Make a from one or more DSP intervals. The interval(s) can be provided as string or as
+ PropertyElement with a string inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the value is not a valid DSP interval, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more DSP intervals, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_interval_prop(":testproperty", "61:3600")
@@ -1021,7 +928,7 @@ def make_interval_prop(
61:3600
- >>> make_interval_prop(":testproperty", values=["61:3600", "60.5:120.5"])
+ >>> make_interval_prop(":testproperty", ["61:3600", "60.5:120.5"])
61:360060.5:120.5
@@ -1031,25 +938,21 @@ def make_interval_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.match(r"([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)):([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+))", str(val.value)):
- raise BaseError(f"Invalid integer format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
-
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid DSP interval.")
+ # make xml structure of the valid values
prop_ = etree.Element(
"{%s}interval-prop" % (xml_namespace_map[None]),
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -1067,25 +970,23 @@ def make_interval_prop(
def make_list_prop(
list_name: str,
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
- Make a from one or more list items. The list item(s) can be provided as string or as PropertyElement
- with a string inside. If provided as string, the permission for every value is "prop-default".
+ Make a from one or more list nodes. The name(s) of the list node(s) can be provided as string or as
+ PropertyElement with a string inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use ``values``.
+ If the name of one of the list nodes is not a valid string, a BaseError will be raised.
Args:
list_name: the name of the list as defined in the onto
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more node names, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_list_prop("mylist", ":testproperty", "first_node")
@@ -1096,7 +997,7 @@ def make_list_prop(
first_node
- >>> make_list_prop("mylist", ":testproperty", values=["first_node", "second_node"])
+ >>> make_list_prop("mylist", ":testproperty", ["first_node", "second_node"])
first_nodesecond_node
@@ -1106,17 +1007,13 @@ def make_list_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not isinstance(val.value, str) or not check_notna(val.value):
- raise BaseError(f"Invalid list format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid name of a list node.")
# make xml structure of the valid values
prop_ = etree.Element(
@@ -1125,7 +1022,7 @@ def make_list_prop(
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -1142,25 +1039,22 @@ def make_list_prop(
def make_resptr_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
- Make a from one or more links to other resources. The links(s) can be provided as string or as
- PropertyElement with a string inside. If provided as string, the permission for every value is "prop-default".
+ Make a from one or more IDs of other resources. The ID(s) can be provided as string or as
+ PropertyElement with a string inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use
- ``values``.
+ If the ID of one of the target resources is not a valid string, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more resource identifiers, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_resptr_prop(":testproperty", "resource_1")
@@ -1171,7 +1065,7 @@ def make_resptr_prop(
resource_1
- >>> make_resptr_prop(":testproperty", values=["resource_1", "resource_2"])
+ >>> make_resptr_prop(":testproperty", ["resource_1", "resource_2"])
resource_1resource_2
@@ -1181,17 +1075,13 @@ def make_resptr_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not isinstance(val.value, str) or not check_notna(val.value):
- raise BaseError(f"Invalid resptr format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Validation Error in resource '{calling_resource}', property '{name}': "
+ f"The following doesn't seem to be a valid ID of a target resource: '{val.value}'")
# make xml structure of the valid values
prop_ = etree.Element(
@@ -1199,7 +1089,7 @@ def make_resptr_prop(
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -1216,25 +1106,22 @@ def make_resptr_prop(
def make_text_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
- Make a from one or more texts. The text(s) can be provided as string or as PropertyElement with a string
- inside. The default encoding is utf8. The default permission for every value is "prop-default".
+ Make a from one or more strings. The string(s) can be provided as string or as PropertyElement with a
+ string inside. If provided as string, the encoding defaults to utf8, and the permissions to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ````
- children, use ``values``.
+ If the value is not a valid string, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more strings, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_text_prop(":testproperty", "first text")
@@ -1245,7 +1132,7 @@ def make_text_prop(
first text
- >>> make_text_prop(":testproperty", values=["first text", "second text"])
+ >>> make_text_prop(":testproperty", ["first text", "second text"])
first textsecond text
@@ -1255,17 +1142,13 @@ def make_text_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not isinstance(val.value, str) or not check_notna(val.value):
- raise BaseError(f"Invalid text format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid string.")
# make xml structure of the valid values
prop_ = etree.Element(
@@ -1273,7 +1156,7 @@ def make_text_prop(
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -1294,26 +1177,23 @@ def make_text_prop(
def make_time_prop(
name: str,
- value: Optional[Union[PropertyElement, str]] = None,
- values: Optional[Iterable[Union[PropertyElement, str]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make a from one or more datetime values of the form "2009-10-10T12:00:00-05:00". The time(s) can be
- provided as string or as PropertyElement with a string inside. If provided as string, the permission for every
- value is "prop-default".
+ provided as string or as PropertyElement with a string inside. If provided as string, the permissions default to
+ "prop-default".
- To create one ``
- >>> make_time_prop(":testproperty", values=["2009-10-10T12:00:00-05:00", "1901-01-01T01:00:00-00:00"])
+ >>> make_time_prop(":testproperty", ["2009-10-10T12:00:00-05:00", "1901-01-01T01:00:00-00:00"])
2009-10-10T12:00:00-05:00
@@ -1342,17 +1222,13 @@ def make_time_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
if not re.search(r"^\d{4}-[0-1]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(.\d{1,12})?(Z|[+-][0-1]\d:[0-5]\d)$", str(val.value)):
- raise BaseError(f"Invalid time format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid DSP time.")
# make xml structure of the valid values
prop_ = etree.Element(
@@ -1360,7 +1236,7 @@ def make_time_prop(
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -1377,24 +1253,22 @@ def make_time_prop(
def make_uri_prop(
name: str,
- value: Optional[Union[PropertyElement, str, Any]] = None,
- values: Optional[Iterable[Union[PropertyElement, str, Any]]] = None,
+ value: Union[PropertyElement, str, Iterable[Union[PropertyElement, str]]],
calling_resource: str = ""
-) -> etree.Element:
+) -> etree._Element:
"""
Make an from one or more URIs. The URI(s) can be provided as string or as PropertyElement with a string
- inside. If provided as string, the permission for every value is "prop-default".
+ inside. If provided as string, the permissions default to "prop-default".
- To create one ```` child, use the param ``value``, to create more than one ```` children, use ``values``.
+ If one of the values is not a valid URI, a BaseError will be raised.
Args:
name: the name of this property as defined in the onto
- value: a string/PropertyElement
- values: an iterable of (usually distinct) strings/PropertyElements
+ value: one or more URIs, as string/PropertyElement, or as iterable of strings/PropertyElements
calling_resource: the name of the parent resource (for better error messages)
Returns:
- an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...))
+ an etree._Element that can be appended to the parent resource with resource.append(make_*_prop(...))
Examples:
>>> make_uri_prop(":testproperty", "www.test.com")
@@ -1405,7 +1279,7 @@ def make_uri_prop(
www.test.com
- >>> make_uri_prop(":testproperty", values=["www.1.com", "www.2.com"])
+ >>> make_uri_prop(":testproperty", ["www.1.com", "www.2.com"])
www.1.comwww.2.com
@@ -1415,20 +1289,16 @@ def make_uri_prop(
"""
# check the input: prepare a list with valid values
- values_new = _check_and_prepare_values(
- value=value,
- values=values,
- name=name,
- calling_resource=calling_resource
- )
+ values = prepare_value(value)
# check value type
- for val in values_new:
+ for val in values:
# URI = scheme ":" ["//" host [":" port]] path ["?" query] ["#" fragment]
if not regex.search(
r"(?[a-z][a-z0-9+.\-]*):(//(?[\w_.\-\[\]:~]+)(?:\d{0,6})?)(?/[\p{L}%()_\-.~]*)*"
r"(?\?[\p{L}_.\-=]+)*(?#[\p{L}_/\-~:.]*)?", str(val.value), flags=regex.UNICODE):
- raise BaseError(f"Invalid URI format for prop '{name}' in resource '{calling_resource}': '{val.value}'")
+ raise BaseError(f"Failed validation in resource '{calling_resource}', property '{name}': "
+ f"'{val.value}' is not a valid URI.")
# make xml structure of the valid values
prop_ = etree.Element(
@@ -1436,7 +1306,7 @@ def make_uri_prop(
name=name,
nsmap=xml_namespace_map
)
- for val in values_new:
+ for val in values:
kwargs = {"permissions": val.permissions}
if check_notna(val.comment):
kwargs["comment"] = val.comment
@@ -1457,7 +1327,7 @@ def make_region(
permissions: str = "res-default",
ark: Optional[str] = None,
iri: Optional[str] = None
-) -> etree.Element:
+) -> etree._Element:
"""
Creates an empty region element, with the attributes as specified by the arguments
@@ -1506,7 +1376,7 @@ def make_annotation(
permissions: str = "res-default",
ark: Optional[str] = None,
iri: Optional[str] = None
-) -> etree.Element:
+) -> etree._Element:
"""
Creates an empty annotation element, with the attributes as specified by the arguments
@@ -1553,7 +1423,7 @@ def make_link(
permissions: str = "res-default",
ark: Optional[str] = None,
iri: Optional[str] = None
-) -> etree.Element:
+) -> etree._Element:
"""
Creates an empty link element, with the attributes as specified by the arguments
@@ -1911,10 +1781,10 @@ def excel2xml(datafile: str, shortcode: str, default_ontology: str) -> None:
"name": row["prop name"],
"calling_resource": resource_id
}
- if len(property_elements) == 1:
+ if make_prop_function in single_value_functions and len(property_elements) == 1:
kwargs_propfunc["value"] = property_elements[0]
else:
- kwargs_propfunc["values"] = property_elements
+ kwargs_propfunc["value"] = property_elements
if check_notna(row["prop list"]):
kwargs_propfunc["list_name"] = str(row["prop list"])
diff --git a/test/unittests/test_excel2xml.py b/test/unittests/test_excel2xml.py
index a897e4b01..fa23476be 100644
--- a/test/unittests/test_excel2xml.py
+++ b/test/unittests/test_excel2xml.py
@@ -3,7 +3,6 @@
import re
from typing import Callable, Sequence, Union, Optional, Any
-import pandas as pd
import numpy as np
import pytest
from lxml import etree
@@ -36,51 +35,59 @@ def run_test(
max = len(different_values)
# prepare the test cases of the form (expected_xml, kwargs for the method to generate XML)
- testcases: list[tuple[str, dict[str, Any]]] = [
- (
- f'<{prop}-prop name=":test"><{prop} permissions="prop-default">{different_values[0 % max]}'
- f'{prop}>{prop}-prop>',
- dict(name=":test", value=different_values[0 % max])
- ),
- (
- f'<{prop}-prop name=":test"><{prop} permissions="prop-restricted">{different_values[1 % max]}'
- f'{prop}>{prop}-prop>',
- dict(name=":test", value=excel2xml.PropertyElement(different_values[1 % max], permissions="prop-restricted"))
- ),
- (
- f'<{prop}-prop name=":test"><{prop} permissions="prop-default" comment="comment">{different_values[2 % max]}'
- f'{prop}>{prop}-prop>',
- dict(name=":test", value=excel2xml.PropertyElement(different_values[2 % max], comment="comment"))
- ),
+ testcases: list[tuple[str, dict[str, Any]]] = list()
+ # pass every element of different_values separately
+ for val in different_values:
+ testcases.extend(
+ [
+ (
+ f'<{prop}-prop name=":test"><{prop} permissions="prop-default">{val}'
+ f'{prop}>{prop}-prop>',
+ dict(name=":test", value=val)
+ ),
+ (
+ f'<{prop}-prop name=":test"><{prop} permissions="prop-restricted">{val}'
+ f'{prop}>{prop}-prop>',
+ dict(name=":test", value=excel2xml.PropertyElement(val, permissions="prop-restricted"))
+ ),
+ (
+ f'<{prop}-prop name=":test"><{prop} permissions="prop-restricted" comment="comment">{val}'
+ f'{prop}>{prop}-prop>',
+ dict(name=":test", value=excel2xml.PropertyElement(val, permissions="prop-restricted", comment="comment"))
+ )
+ ]
+ )
+ # pass the elements of different_values group-wise
+ testcases.extend([
(
f'<{prop}-prop name=":test">'
f'<{prop} permissions="prop-default">{identical_values[0]}{prop}>'
- f'<{prop} permissions="prop-default">{identical_values[0]}{prop}>'
- f'<{prop} permissions="prop-default">{identical_values[0]}{prop}>'
+ f'<{prop} permissions="prop-default">{identical_values[1]}{prop}>'
+ f'<{prop} permissions="prop-default">{identical_values[2]}{prop}>'
f'{prop}-prop>',
- dict(name=":test", values=identical_values)
+ dict(name=":test", value=identical_values)
),
(
f'<{prop}-prop name=":test">'
- f'<{prop} permissions="prop-default">{different_values[3 % max]}{prop}>'
- f'<{prop} permissions="prop-default">{different_values[4 % max]}{prop}>'
- f'<{prop} permissions="prop-default">{different_values[5 % max]}{prop}>'
+ f'<{prop} permissions="prop-default">{different_values[0 % max]}{prop}>'
+ f'<{prop} permissions="prop-default">{different_values[1 % max]}{prop}>'
+ f'<{prop} permissions="prop-default">{different_values[2 % max]}{prop}>'
f'{prop}-prop>',
- dict(name=":test", values=[different_values[3 % max], different_values[4 % max], different_values[5 % max]])
+ dict(name=":test", value=[different_values[0 % max], different_values[1 % max], different_values[2 % max]])
),
(
f'<{prop}-prop name=":test">'
- f'<{prop} permissions="prop-restricted" comment="comment1">{different_values[6 % max]}{prop}>'
- f'<{prop} permissions="prop-default" comment="comment2">{different_values[7 % max]}{prop}>'
- f'<{prop} permissions="prop-restricted" comment="comment3">{different_values[8 % max]}{prop}>'
+ f'<{prop} permissions="prop-restricted" comment="comment1">{different_values[3 % max]}{prop}>'
+ f'<{prop} permissions="prop-default" comment="comment2">{different_values[4 % max]}{prop}>'
+ f'<{prop} permissions="prop-restricted" comment="comment3">{different_values[5 % max]}{prop}>'
f'{prop}-prop>',
- dict(name=":test", values=[
- excel2xml.PropertyElement(different_values[6 % max], permissions="prop-restricted", comment="comment1"),
- excel2xml.PropertyElement(different_values[7 % max], permissions="prop-default", comment="comment2"),
- excel2xml.PropertyElement(different_values[8 % max], permissions="prop-restricted", comment="comment3")
+ dict(name=":test", value=[
+ excel2xml.PropertyElement(different_values[3 % max], permissions="prop-restricted", comment="comment1"),
+ excel2xml.PropertyElement(different_values[4 % max], permissions="prop-default", comment="comment2"),
+ excel2xml.PropertyElement(different_values[5 % max], permissions="prop-restricted", comment="comment3")
])
)
- ]
+ ])
# run the test cases
for tc in testcases:
@@ -101,14 +108,6 @@ def run_test(
msg=f"Method {method.__name__} failed with kwargs {kwargs_to_generate_xml}")
# perform illegal actions
- # pass iterable of different values as param "value"
- kwargs_value_with_different_values = dict(name=":test", value=different_values)
- if prop == "list":
- kwargs_value_with_different_values["list_name"] = listname
- with testcase.assertRaises(BaseError, msg=f"Method {method.__name__} failed with kwargs "
- f"{kwargs_value_with_different_values}"):
- method(**kwargs_value_with_different_values)
-
# pass invalid values as param "value"
for invalid_value in invalid_values:
kwargs_invalid_value = dict(name=":test", value=invalid_value)
@@ -147,7 +146,7 @@ def test_find_date_in_string(self) -> None:
self.assertEqual(excel2xml.find_date_in_string("text 1492-10-12, text"), "GREGORIAN:CE:1492-10-12:CE:1492-10-12")
self.assertEqual(excel2xml.find_date_in_string("Text 0476-09-04. text"), "GREGORIAN:CE:0476-09-04:CE:0476-09-04")
self.assertEqual(excel2xml.find_date_in_string("Text (0476-09-04) text"), "GREGORIAN:CE:0476-09-04:CE:0476-09-04")
- self.assertWarns(UserWarning, lambda: excel2xml.find_date_in_string("Text [1492-10-32?] text"))
+ self.assertIsNone(excel2xml.find_date_in_string("Text [1492-10-32?] text"))
# template: 31.4.2021 | 5/11/2021
self.assertEqual(excel2xml.find_date_in_string("Text (30.4.2021) text"), "GREGORIAN:CE:2021-04-30:CE:2021-04-30")
@@ -156,28 +155,28 @@ def test_find_date_in_string(self) -> None:
# template: 26.2.-24.3.1948
self.assertEqual(excel2xml.find_date_in_string("Text ...2193_01_26... text"), "GREGORIAN:CE:2193-01-26:CE:2193-01-26")
self.assertEqual(excel2xml.find_date_in_string("Text -2193_01_26- text"), "GREGORIAN:CE:2193-01-26:CE:2193-01-26")
- self.assertWarns(UserWarning, lambda: excel2xml.find_date_in_string("Text 2193_02_30 text"))
+ self.assertIsNone(excel2xml.find_date_in_string("Text 2193_02_30 text"))
# template: 27.-28.1.1900
self.assertEqual(excel2xml.find_date_in_string("Text _1.3. - 25.4.2022_ text"), "GREGORIAN:CE:2022-03-01:CE:2022-04-25")
self.assertEqual(excel2xml.find_date_in_string("Text (01.03. - 25.04.2022) text"), "GREGORIAN:CE:2022-03-01:CE:2022-04-25")
self.assertEqual(excel2xml.find_date_in_string("Text 28.2.-1.12.1515 text"), "GREGORIAN:CE:1515-02-28:CE:1515-12-01")
self.assertEqual(excel2xml.find_date_in_string("Text 28.2.-1.12.1515 text"), "GREGORIAN:CE:1515-02-28:CE:1515-12-01")
- self.assertWarns(UserWarning, lambda: excel2xml.find_date_in_string("Text 28.2.-26.2.1515 text"))
+ self.assertIsNone(excel2xml.find_date_in_string("Text 28.2.-26.2.1515 text"))
# template: 1.12.1973 - 6.1.1974
self.assertEqual(excel2xml.find_date_in_string("Text 25.-26.2.0800 text"), "GREGORIAN:CE:0800-02-25:CE:0800-02-26")
self.assertEqual(excel2xml.find_date_in_string("Text 25. - 26.2.0800 text"), "GREGORIAN:CE:0800-02-25:CE:0800-02-26")
self.assertEqual(excel2xml.find_date_in_string("Text 25. - 26.2.0800 text"), "GREGORIAN:CE:0800-02-25:CE:0800-02-26")
- self.assertWarns(UserWarning, lambda: excel2xml.find_date_in_string("Text 25.-24.2.0800 text"))
+ self.assertIsNone(excel2xml.find_date_in_string("Text 25.-24.2.0800 text"))
# template: 31.4.2021 | 5/11/2021
self.assertEqual(excel2xml.find_date_in_string("Text 1.9.2022-3.1.2024 text"), "GREGORIAN:CE:2022-09-01:CE:2024-01-03")
self.assertEqual(excel2xml.find_date_in_string("Text 25.12.2022 - 3.1.2024 text"), "GREGORIAN:CE:2022-12-25:CE:2024-01-03")
- self.assertWarns(UserWarning, lambda: excel2xml.find_date_in_string("Text 25.12.2022-03.01.2022 text"))
+ self.assertIsNone(excel2xml.find_date_in_string("Text 25.12.2022-03.01.2022 text"))
self.assertEqual(excel2xml.find_date_in_string("Text 25/12/2022-03/01/2024 text"), "GREGORIAN:CE:2022-12-25:CE:2024-01-03")
self.assertEqual(excel2xml.find_date_in_string("Text 25/12/2022 - 3/1/2024 text"), "GREGORIAN:CE:2022-12-25:CE:2024-01-03")
- self.assertWarns(UserWarning, lambda: excel2xml.find_date_in_string("Text 25/12/2022-03/01/2022 text"))
+ self.assertIsNone(excel2xml.find_date_in_string("Text 25/12/2022-03/01/2022 text"))
# template: February 9, 1908 | Dec 5,1908
self.assertEqual(excel2xml.find_date_in_string("Text Jan 26, 1993 text"), "GREGORIAN:CE:1993-01-26:CE:1993-01-26")
@@ -198,81 +197,35 @@ def test_find_date_in_string(self) -> None:
self.assertEqual(excel2xml.find_date_in_string("Text [1849/1850] text"), "GREGORIAN:CE:1849:CE:1850")
- def test_check_and_prepare_values(self) -> None:
+ def test_prepare_value(self) -> None:
identical_values = ["Test", "Test", "Test"]
different_values: list[Union[str, int, float]] = [1, 1.0, "1", "1.0", " 1 "]
values_with_nas: list[Union[str, int, float]] = ["test", "", 1, np.nan, 0]
for val in different_values:
- values_output = excel2xml._check_and_prepare_values(
- value=val,
- values=None,
- name=""
- )
- self.assertEqual([x.value for x in values_output], [val,])
-
- values_output = excel2xml._check_and_prepare_values(
- value=excel2xml.PropertyElement(val),
- values=None,
- name=""
- )
- self.assertEqual([x.value for x in values_output], [val,])
-
- values_output = excel2xml._check_and_prepare_values(
- value=None,
- values=identical_values,
- name=""
- )
+ values_output = excel2xml.prepare_value(val)
+ self.assertEqual([x.value for x in values_output], [val, ])
+
+ values_output = excel2xml.prepare_value(excel2xml.PropertyElement(val))
+ self.assertEqual([x.value for x in values_output], [val, ])
+
+ values_output = excel2xml.prepare_value(identical_values)
self.assertEqual([x.value for x in values_output], identical_values)
- values_output = excel2xml._check_and_prepare_values(
- value=None,
- values=[excel2xml.PropertyElement(x) for x in identical_values],
- name=""
- )
+ values_output = excel2xml.prepare_value([excel2xml.PropertyElement(x) for x in identical_values])
self.assertEqual([x.value for x in values_output], identical_values)
- values_output = excel2xml._check_and_prepare_values(
- value=None,
- values=different_values,
- name=""
- )
+ values_output = excel2xml.prepare_value(different_values)
self.assertEqual([x.value for x in values_output], different_values)
- values_output = excel2xml._check_and_prepare_values(
- value=None,
- values=[excel2xml.PropertyElement(x) for x in different_values],
- name=""
- )
+ values_output = excel2xml.prepare_value([excel2xml.PropertyElement(x) for x in different_values])
self.assertEqual([x.value for x in values_output], different_values)
- values_output = excel2xml._check_and_prepare_values(
- value=None,
- values=values_with_nas,
- name=""
- )
- self.assertEqual([x.value for x in values_output], ["test", 1, 0])
-
- self.assertRaises(BaseError, lambda: excel2xml._check_and_prepare_values(
- value=different_values,
- values=None,
- name=""
- ))
- self.assertRaises(BaseError, lambda: excel2xml._check_and_prepare_values(
- value=[excel2xml.PropertyElement(x) for x in different_values],
- values=None,
- name=""
- ))
- self.assertRaises(BaseError, lambda: excel2xml._check_and_prepare_values(
- value=1,
- values=[1],
- name=""
- ))
- self.assertRaises(BaseError, lambda: excel2xml._check_and_prepare_values(
- value=np.nan,
- values=[np.nan],
- name=""
- ))
+ values_output = excel2xml.prepare_value(values_with_nas)
+ self.assertEqual([x.value for x in values_output], values_with_nas)
+
+ values_output = excel2xml.prepare_value([excel2xml.PropertyElement(x) for x in values_with_nas])
+ self.assertEqual([x.value for x in values_output], values_with_nas)
def test_make_boolean_prop(self) -> None:
diff --git a/test/unittests/test_shared.py b/test/unittests/test_shared.py
index c78fa8a84..b3a0924eb 100644
--- a/test/unittests/test_shared.py
+++ b/test/unittests/test_shared.py
@@ -8,6 +8,7 @@
class TestShared(unittest.TestCase):
+
def test_validate_xml_against_schema(self) -> None:
self.assertTrue(shared.validate_xml_against_schema("testdata/test-data-systematic.xml"))
self.assertTrue(shared.validate_xml_against_schema("testdata/test-data-minimal.xml"))
@@ -18,6 +19,7 @@ def test_validate_xml_against_schema(self) -> None:
):
shared.validate_xml_against_schema("testdata/test-data-invalid-resource-tag.xml")
+
def test_prepare_dataframe(self) -> None:
original_df = pd.DataFrame({
" TitLE of Column 1 ": ["1", " 0-1 ", "1-n ", pd.NA, " ", " ", "", " 0-n ", np.nan],
@@ -41,7 +43,8 @@ def test_prepare_dataframe(self) -> None:
def test_check_notna(self) -> None:
- na_values = [None, pd.NA, np.nan, "", " ", "-", ",", ".", "*", "!", " ⳰", " ῀ ", " ῾ ", " \n\t ", "N/A", "n/a",
+ na_values = [None, pd.NA, np.nan, "", " ", "-", ",", ".", "*", " ⳰", " ῀ ", " ῾ ", " \n\t ", "N/A", "n/a",
+
"", ["a", "b"], pd.array(["a", "b"]), np.array([0, 1])]
for na_value in na_values:
self.assertFalse(shared.check_notna(na_value), msg=f"Failed na_value: {na_value}")