From f97cce80be17f23df15fd9dca220b3c52f2d58cc Mon Sep 17 00:00:00 2001 From: Johannes Nussbaum <39048939+jnussbaum@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:44:48 +0100 Subject: [PATCH] refactor: split up models/helpers.py (#760) --- pyproject.toml | 8 +- .../commands/excel2xml/excel2xml_lib.py | 2 +- .../commands/project/create/project_create.py | 4 +- .../project/models/context.py} | 131 +----------------- .../commands/project/models/group.py | 2 +- .../commands/project/models/helpers.py | 54 ++++++++ .../commands/project/models/listnode.py | 2 +- .../commands/project/models/ontology.py | 4 +- .../commands/project/models/project.py | 2 +- .../commands/project/models/propertyclass.py | 4 +- .../commands/project/models/resourceclass.py | 4 +- src/dsp_tools/commands/project/models/user.py | 2 +- .../commands/xmlupload/models/xmlallow.py | 3 - .../commands/xmlupload/models/xmlresource.py | 8 +- src/dsp_tools/models/datetimestamp.py | 81 +++++++++++ src/dsp_tools/utils/set_encoder.py | 3 +- test/e2e/commands/project/test_ontology.py | 2 +- 17 files changed, 166 insertions(+), 150 deletions(-) rename src/dsp_tools/{models/helpers.py => commands/project/models/context.py} (77%) create mode 100644 src/dsp_tools/commands/project/models/helpers.py create mode 100644 src/dsp_tools/models/datetimestamp.py diff --git a/pyproject.toml b/pyproject.toml index 510622b47..71223cd63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,8 +73,6 @@ check-links = """ """ darglint = """ find . -name "*.py" \ - -not -path "./src/dsp_tools/models/*" \ - -not -path "./src/dsp_tools/commands/xmlupload/models/*" \ -not -path "./src/dsp_tools/commands/project/models/*" \ -not -path "./.git/*" \ -not -path "./.venv/*" \ @@ -132,13 +130,15 @@ ignore_missing_imports = true # TODO: deactivate show_column_numbers = true strict = true exclude = [ - "src/dsp_tools/models/helpers.py", # TODO: activate this + "src/dsp_tools/models/datetimestamp.py", # TODO: activate this "src/dsp_tools/models/langstring.py", # TODO: activate this + "src/dsp_tools/models/projectContext.py", # TODO: activate this + "src/dsp_tools/commands/project/models/context.py", # TODO: activate this "src/dsp_tools/commands/project/models/group.py", # TODO: activate this + "src/dsp_tools/commands/project/models/helpers.py", # TODO: activate this "src/dsp_tools/commands/project/models/listnode.py", # TODO: activate this "src/dsp_tools/commands/project/models/ontology.py", # TODO: activate this "src/dsp_tools/commands/project/models/project.py", # TODO: activate this - "src/dsp_tools/models/projectContext.py", # TODO: activate this "src/dsp_tools/commands/project/models/propertyclass.py", # TODO: activate this "src/dsp_tools/commands/project/models/resourceclass.py", # TODO: activate this "src/dsp_tools/commands/project/models/user.py", # TODO: activate this diff --git a/src/dsp_tools/commands/excel2xml/excel2xml_lib.py b/src/dsp_tools/commands/excel2xml/excel2xml_lib.py index d623f10d2..8cb15783f 100644 --- a/src/dsp_tools/commands/excel2xml/excel2xml_lib.py +++ b/src/dsp_tools/commands/excel2xml/excel2xml_lib.py @@ -14,8 +14,8 @@ from regex import Match from dsp_tools.commands.excel2xml.propertyelement import PropertyElement +from dsp_tools.models.datetimestamp import DateTimeStamp from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import DateTimeStamp from dsp_tools.utils.date_util import is_full_date from dsp_tools.utils.shared import check_notna, simplify_name, validate_xml_against_schema from dsp_tools.utils.uri_util import is_uri diff --git a/src/dsp_tools/commands/project/create/project_create.py b/src/dsp_tools/commands/project/create/project_create.py index 5cb3876a0..e243668e7 100644 --- a/src/dsp_tools/commands/project/create/project_create.py +++ b/src/dsp_tools/commands/project/create/project_create.py @@ -9,14 +9,16 @@ from dsp_tools.commands.excel2json.lists import expand_lists_from_excel from dsp_tools.commands.project.create.project_create_lists import create_lists_on_server from dsp_tools.commands.project.create.project_validate import validate_project +from dsp_tools.commands.project.models.context import Context from dsp_tools.commands.project.models.group import Group +from dsp_tools.commands.project.models.helpers import Cardinality from dsp_tools.commands.project.models.ontology import Ontology from dsp_tools.commands.project.models.project import Project from dsp_tools.commands.project.models.propertyclass import PropertyClass from dsp_tools.commands.project.models.resourceclass import ResourceClass from dsp_tools.commands.project.models.user import User +from dsp_tools.models.datetimestamp import DateTimeStamp from dsp_tools.models.exceptions import BaseError, UserError -from dsp_tools.models.helpers import Cardinality, Context, DateTimeStamp from dsp_tools.models.langstring import LangString from dsp_tools.utils.connection import Connection from dsp_tools.utils.connection_live import ConnectionLive diff --git a/src/dsp_tools/models/helpers.py b/src/dsp_tools/commands/project/models/context.py similarity index 77% rename from src/dsp_tools/models/helpers.py rename to src/dsp_tools/commands/project/models/context.py index f3258d0a1..77008103c 100644 --- a/src/dsp_tools/models/helpers.py +++ b/src/dsp_tools/commands/project/models/context.py @@ -1,54 +1,16 @@ from __future__ import annotations -from dataclasses import dataclass -from enum import Enum, unique -from typing import Any, Optional, Pattern, Union +from typing import Optional, Pattern import regex +from dsp_tools.commands.project.models.helpers import ContextType, OntoIri from dsp_tools.models.exceptions import BaseError from dsp_tools.utils.iri_util import is_iri -# -# here we do some data typing that should help -# - - -@dataclass(frozen=True) -class OntoIri: - """ - Holds an ontology IRI - - Attributes: - iri: the ontology IRI - hashtag: True if "#" is used to separate elements, False if element name is appended after "/" - """ - - iri: str - hashtag: bool - - -ContextType = dict[str, OntoIri] - - -@unique -class Actions(Enum): - Create = 1 - Read = 2 - Update = 3 - Delete = 4 - - -@unique -class Cardinality(Enum): - C_1 = "1" - C_0_1 = "0-1" - C_1_n = "1-n" - C_0_n = "0-n" - class ContextIterator: - _context: "Context" + _context: Context _prefixes: list[str] _index: int @@ -367,90 +329,3 @@ def toJsonObj(self) -> dict[str, str]: def get_externals_used(self) -> dict[str, str]: exclude = ["rdf", "rdfs", "owl", "xsd", "knora-api", "salsah-gui"] return {prefix: onto.iri for prefix, onto in self._context.items() if prefix not in exclude} - - -class DateTimeStamp: - """ - Class to hold and process an xsd:dateTimeStamp - """ - - _dateTimeStamp: str - _validation_regex = ( - r"^-?([1-9][0-9]{3,}|0[0-9]{3})" - r"-(0[1-9]|1[0-2])" - r"-(0[1-9]|[12][0-9]|3[01])" - r"T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?|(24:00:00(\.0+)?))" - r"(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))$" - ) - - def __init__(self, val: Any): - """ - The constructor works for different inputs: - - a string - - an instance of "DateTimeStamp" - - json-ld construct of the form { "@type": "xsd:dateTimeStamp", "@value": "date-str" } - :param val: xsd:dateTimeStamp as string, instance of "DateTimeStamp" or json-ld construct - """ - if isinstance(val, str): - if not regex.search(self._validation_regex, val): - raise BaseError(f"Invalid xsd:dateTimeStamp: '{val}'") - self._dateTimeStamp = val - elif isinstance(val, DateTimeStamp): - self._dateTimeStamp = str(val) - else: - if val.get("@type") == "xsd:dateTimeStamp" and regex.search(self._validation_regex, str(val.get("@value"))): - self._dateTimeStamp = val["@value"] - else: - raise BaseError(f"Invalid xsd:dateTimeStamp: '{val}'") - - def __eq__(self, other: Union[str, "DateTimeStamp"]) -> bool: - if isinstance(other, str): - other = DateTimeStamp(other) - return self._dateTimeStamp == other._dateTimeStamp - - def __lt__(self, other: "DateTimeStamp") -> bool: - if isinstance(other, str): - other = DateTimeStamp(other) - return self._dateTimeStamp < other._dateTimeStamp - - def __le__(self, other: "DateTimeStamp") -> bool: - if isinstance(other, str): - other = DateTimeStamp(other) - return self._dateTimeStamp <= other._dateTimeStamp - - def __gt__(self, other: "DateTimeStamp") -> bool: - if isinstance(other, str): - other = DateTimeStamp(other) - return self._dateTimeStamp > other._dateTimeStamp - - def __ge__(self, other: "DateTimeStamp") -> bool: - if isinstance(other, str): - other = DateTimeStamp(other) - return self._dateTimeStamp >= other._dateTimeStamp - - def __ne__(self, other: DateTimeStamp) -> bool: - if isinstance(other, str): - other = DateTimeStamp(other) - return self._dateTimeStamp != other._dateTimeStamp - - def __str__(self: "DateTimeStamp") -> str: - return self._dateTimeStamp - - def toJsonObj(self) -> dict[str, str]: - return {"@type": "xsd:dateTimeStamp", "@value": self._dateTimeStamp} - - -class WithId: - """ - Class helper to get json-ld "@id" thingies - """ - - _tmp: str = None - - def __init__(self, obj: Optional[dict[str, str]]): - if obj is None: - return - self._tmp = obj.get("@id") - - def to_string(self) -> Optional[str]: - return self._tmp diff --git a/src/dsp_tools/commands/project/models/group.py b/src/dsp_tools/commands/project/models/group.py index fe7cea705..f3b1b8e2a 100644 --- a/src/dsp_tools/commands/project/models/group.py +++ b/src/dsp_tools/commands/project/models/group.py @@ -26,10 +26,10 @@ from typing import Any, Optional, Union from urllib.parse import quote_plus +from dsp_tools.commands.project.models.helpers import Actions from dsp_tools.commands.project.models.model import Model from dsp_tools.commands.project.models.project import Project from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions from dsp_tools.models.langstring import LangString from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/project/models/helpers.py b/src/dsp_tools/commands/project/models/helpers.py new file mode 100644 index 000000000..0873be690 --- /dev/null +++ b/src/dsp_tools/commands/project/models/helpers.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum, unique +from typing import Optional + + +@dataclass(frozen=True) +class OntoIri: + """ + Holds an ontology IRI + + Attributes: + iri: the ontology IRI + hashtag: True if "#" is used to separate elements, False if element name is appended after "/" + """ + + iri: str + hashtag: bool + + +ContextType = dict[str, OntoIri] + + +@unique +class Actions(Enum): + Create = 1 + Read = 2 + Update = 3 + Delete = 4 + + +@unique +class Cardinality(Enum): + C_1 = "1" + C_0_1 = "0-1" + C_1_n = "1-n" + C_0_n = "0-n" + + +class WithId: + """ + Class helper to get json-ld "@id" thingies + """ + + _tmp: str = None + + def __init__(self, obj: Optional[dict[str, str]]): + if obj is None: + return + self._tmp = obj.get("@id") + + def to_string(self) -> Optional[str]: + return self._tmp diff --git a/src/dsp_tools/commands/project/models/listnode.py b/src/dsp_tools/commands/project/models/listnode.py index 0897f9be8..fc7ef2fa7 100644 --- a/src/dsp_tools/commands/project/models/listnode.py +++ b/src/dsp_tools/commands/project/models/listnode.py @@ -25,10 +25,10 @@ from typing import Any, Optional, Union from urllib.parse import quote_plus +from dsp_tools.commands.project.models.helpers import Actions from dsp_tools.commands.project.models.model import Model from dsp_tools.commands.project.models.project import Project from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions from dsp_tools.models.langstring import LangString, Languages from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/project/models/ontology.py b/src/dsp_tools/commands/project/models/ontology.py index 6cb2bd3c1..87a762cb3 100644 --- a/src/dsp_tools/commands/project/models/ontology.py +++ b/src/dsp_tools/commands/project/models/ontology.py @@ -32,12 +32,14 @@ import regex +from dsp_tools.commands.project.models.context import Context +from dsp_tools.commands.project.models.helpers import Actions, WithId from dsp_tools.commands.project.models.model import Model from dsp_tools.commands.project.models.project import Project from dsp_tools.commands.project.models.propertyclass import PropertyClass from dsp_tools.commands.project.models.resourceclass import ResourceClass +from dsp_tools.models.datetimestamp import DateTimeStamp from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions, Context, DateTimeStamp, WithId from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/project/models/project.py b/src/dsp_tools/commands/project/models/project.py index dcadd4f8e..01102952f 100644 --- a/src/dsp_tools/commands/project/models/project.py +++ b/src/dsp_tools/commands/project/models/project.py @@ -27,9 +27,9 @@ from typing import Any, Optional, Union from urllib.parse import quote_plus +from dsp_tools.commands.project.models.helpers import Actions from dsp_tools.commands.project.models.model import Model from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions from dsp_tools.models.langstring import LangString, Languages from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/project/models/propertyclass.py b/src/dsp_tools/commands/project/models/propertyclass.py index 26832caf2..7a8cb02bd 100644 --- a/src/dsp_tools/commands/project/models/propertyclass.py +++ b/src/dsp_tools/commands/project/models/propertyclass.py @@ -3,10 +3,12 @@ import regex +from dsp_tools.commands.project.models.context import Context +from dsp_tools.commands.project.models.helpers import Actions, WithId from dsp_tools.commands.project.models.listnode import ListNode from dsp_tools.commands.project.models.model import Model +from dsp_tools.models.datetimestamp import DateTimeStamp from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions, Context, DateTimeStamp, WithId from dsp_tools.models.langstring import LangString, Languages from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/project/models/resourceclass.py b/src/dsp_tools/commands/project/models/resourceclass.py index 395e30976..2d0c4ac7c 100644 --- a/src/dsp_tools/commands/project/models/resourceclass.py +++ b/src/dsp_tools/commands/project/models/resourceclass.py @@ -13,9 +13,11 @@ import regex +from dsp_tools.commands.project.models.context import Context +from dsp_tools.commands.project.models.helpers import Actions, Cardinality from dsp_tools.commands.project.models.model import Model +from dsp_tools.models.datetimestamp import DateTimeStamp from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions, Cardinality, Context, DateTimeStamp from dsp_tools.models.langstring import LangString, Languages from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/project/models/user.py b/src/dsp_tools/commands/project/models/user.py index 8ad1151e9..2111cbb2f 100644 --- a/src/dsp_tools/commands/project/models/user.py +++ b/src/dsp_tools/commands/project/models/user.py @@ -29,10 +29,10 @@ from urllib.parse import quote_plus from dsp_tools.commands.project.models.group import Group +from dsp_tools.commands.project.models.helpers import Actions from dsp_tools.commands.project.models.model import Model from dsp_tools.commands.project.models.project import Project from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions from dsp_tools.models.langstring import Languages from dsp_tools.utils.connection import Connection diff --git a/src/dsp_tools/commands/xmlupload/models/xmlallow.py b/src/dsp_tools/commands/xmlupload/models/xmlallow.py index 6d86c892e..68ead35af 100644 --- a/src/dsp_tools/commands/xmlupload/models/xmlallow.py +++ b/src/dsp_tools/commands/xmlupload/models/xmlallow.py @@ -19,9 +19,6 @@ def __init__(self, node: etree._Element, project_context: ProjectContext) -> Non node: The DOM node to be processed (represents a single right in a permission set) project_context: Context for DOM node traversal - Returns: - None - Raises: XmlUploadError: If an upload fails """ diff --git a/src/dsp_tools/commands/xmlupload/models/xmlresource.py b/src/dsp_tools/commands/xmlupload/models/xmlresource.py index 8d8e6d77e..ec9401a1b 100644 --- a/src/dsp_tools/commands/xmlupload/models/xmlresource.py +++ b/src/dsp_tools/commands/xmlupload/models/xmlresource.py @@ -6,7 +6,7 @@ from dsp_tools.commands.xmlupload.models.permission import Permissions from dsp_tools.commands.xmlupload.models.xmlbitstream import XMLBitstream from dsp_tools.commands.xmlupload.models.xmlproperty import XMLProperty -from dsp_tools.models.helpers import DateTimeStamp +from dsp_tools.models.datetimestamp import DateTimeStamp @dataclass(frozen=True) @@ -56,9 +56,6 @@ def __init__(self, node: etree._Element, default_ontology: str) -> None: Args: node: The DOM node to be processed representing a resource (which is a child of the element) default_ontology: The default ontology (given in the attribute default-ontology of the element) - - Returns: - None """ self.res_id = node.attrib["id"] self.iri = node.attrib.get("iri") @@ -92,6 +89,9 @@ def get_props_with_links(self) -> list[XMLProperty]: """ Get a list of all XMLProperties that have an outgoing link to another resource, be it a resptr-prop link or a standoff link in a text. + + Returns: + list of all XMLProperties """ link_properties: list[XMLProperty] = [] for prop in self.properties: diff --git a/src/dsp_tools/models/datetimestamp.py b/src/dsp_tools/models/datetimestamp.py new file mode 100644 index 000000000..8354ebc66 --- /dev/null +++ b/src/dsp_tools/models/datetimestamp.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from typing import Any, Union + +import regex + +from dsp_tools.models.exceptions import BaseError + + +class DateTimeStamp: + """ + Class to hold and process an xsd:dateTimeStamp + """ + + _dateTimeStamp: str + _validation_regex = ( + r"^-?([1-9][0-9]{3,}|0[0-9]{3})" + r"-(0[1-9]|1[0-2])" + r"-(0[1-9]|[12][0-9]|3[01])" + r"T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?|(24:00:00(\.0+)?))" + r"(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))$" + ) + + def __init__(self, val: Any): + """ + The constructor works for different inputs: a string, an instance of "DateTimeStamp", + or a JSON-LD construct of the form { "@type": "xsd:dateTimeStamp", "@value": "date-str" } + + Args: + val: xsd:dateTimeStamp as string, instance of "DateTimeStamp" or json-ld construct + + Raises: + BaseError: if the JSON-LD construct is not correct + """ + if isinstance(val, str): + if not regex.search(self._validation_regex, val): + raise BaseError(f"Invalid xsd:dateTimeStamp: '{val}'") + self._dateTimeStamp = val + elif isinstance(val, DateTimeStamp): + self._dateTimeStamp = str(val) + else: + if val.get("@type") == "xsd:dateTimeStamp" and regex.search(self._validation_regex, str(val.get("@value"))): + self._dateTimeStamp = val["@value"] + else: + raise BaseError(f"Invalid xsd:dateTimeStamp: '{val}'") + + def __eq__(self, other: Union[str, "DateTimeStamp"]) -> bool: + if isinstance(other, str): + other = DateTimeStamp(other) + return self._dateTimeStamp == other._dateTimeStamp + + def __lt__(self, other: "DateTimeStamp") -> bool: + if isinstance(other, str): + other = DateTimeStamp(other) + return self._dateTimeStamp < other._dateTimeStamp + + def __le__(self, other: "DateTimeStamp") -> bool: + if isinstance(other, str): + other = DateTimeStamp(other) + return self._dateTimeStamp <= other._dateTimeStamp + + def __gt__(self, other: "DateTimeStamp") -> bool: + if isinstance(other, str): + other = DateTimeStamp(other) + return self._dateTimeStamp > other._dateTimeStamp + + def __ge__(self, other: "DateTimeStamp") -> bool: + if isinstance(other, str): + other = DateTimeStamp(other) + return self._dateTimeStamp >= other._dateTimeStamp + + def __ne__(self, other: DateTimeStamp) -> bool: + if isinstance(other, str): + other = DateTimeStamp(other) + return self._dateTimeStamp != other._dateTimeStamp + + def __str__(self: "DateTimeStamp") -> str: + return self._dateTimeStamp + + def toJsonObj(self) -> dict[str, str]: + return {"@type": "xsd:dateTimeStamp", "@value": self._dateTimeStamp} diff --git a/src/dsp_tools/utils/set_encoder.py b/src/dsp_tools/utils/set_encoder.py index b25682c67..6515f1377 100644 --- a/src/dsp_tools/utils/set_encoder.py +++ b/src/dsp_tools/utils/set_encoder.py @@ -1,7 +1,8 @@ import json from typing import Any, Union -from dsp_tools.models.helpers import Context, OntoIri +from dsp_tools.commands.project.models.context import Context +from dsp_tools.commands.project.models.helpers import OntoIri class SetEncoder(json.JSONEncoder): diff --git a/test/e2e/commands/project/test_ontology.py b/test/e2e/commands/project/test_ontology.py index 81100c13e..bf403ed64 100644 --- a/test/e2e/commands/project/test_ontology.py +++ b/test/e2e/commands/project/test_ontology.py @@ -5,7 +5,7 @@ import pytest from dsp_tools.commands.project.models.ontology import Ontology -from dsp_tools.models.helpers import DateTimeStamp +from dsp_tools.models.datetimestamp import DateTimeStamp from dsp_tools.utils.connection import Connection from dsp_tools.utils.connection_live import ConnectionLive