Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xmlupload): check consistency with ontology on server (DEV-2921) #630

Merged
merged 94 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 81 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
1c3b4d0
Create check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 10, 2023
5558e3c
Create consistency_check_model.py
Nora-Olivia-Ammann Nov 10, 2023
1d8d66e
Update check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 10, 2023
5fa5ffd
Update check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 10, 2023
70a33b1
Delete consistency_check_model.py
Nora-Olivia-Ammann Nov 14, 2023
c8c4f3e
Create ontology_client.py
Nora-Olivia-Ammann Nov 14, 2023
c8f345f
Update check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 14, 2023
43a937d
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 14, 2023
e1ef328
Update ontology_client.py
Nora-Olivia-Ammann Nov 14, 2023
25492f6
linting
Nora-Olivia-Ammann Nov 14, 2023
1b7c69a
Update ontology_client.py
Nora-Olivia-Ammann Nov 14, 2023
ca91e05
Update ontology_client.py
Nora-Olivia-Ammann Nov 14, 2023
1ecc03b
moving files
Nora-Olivia-Ammann Nov 14, 2023
c85da41
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 14, 2023
ed32ea4
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 14, 2023
f23203f
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 14, 2023
cb85b9d
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 14, 2023
1d87d13
Update ontology_client.py
Nora-Olivia-Ammann Nov 14, 2023
5ce264f
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 14, 2023
dae232a
Update ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
76868ac
Update ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
3d294c6
Create test_ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
49053bc
clean up ontology client
Nora-Olivia-Ammann Nov 15, 2023
d16c8a3
Update ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
9d8384e
Update ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
5640880
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 15, 2023
020fb78
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 15, 2023
c0ffca7
Testing
Nora-Olivia-Ammann Nov 15, 2023
a12fe21
Update test_ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
2adf97b
Update test_ontology_client.py
Nora-Olivia-Ammann Nov 15, 2023
b728843
get properties and classes from xml
Nora-Olivia-Ammann Nov 15, 2023
897e615
linting
Nora-Olivia-Ammann Nov 16, 2023
0ea9a61
myke mypy happy
Nora-Olivia-Ammann Nov 16, 2023
8bdc00c
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 16, 2023
68fbaf8
add remove prefix
Nora-Olivia-Ammann Nov 16, 2023
43559be
Add Ontology RegEx
Nora-Olivia-Ammann Nov 16, 2023
0d92887
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 16, 2023
9bea108
Update ontology_client.py
Nora-Olivia-Ammann Nov 16, 2023
f1b5ef0
Merge branch 'wip/dev-2921-check-consistency-with-ontology' of https:…
Nora-Olivia-Ammann Nov 16, 2023
7fc0459
Update test_check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 16, 2023
a182069
Update ontology_client.py
Nora-Olivia-Ammann Nov 16, 2023
c4755eb
add unprefixed knora
Nora-Olivia-Ammann Nov 16, 2023
e930e10
simplify regex
Nora-Olivia-Ammann Nov 16, 2023
072b3ae
update with knora
Nora-Olivia-Ammann Nov 16, 2023
0123202
simplify code
Nora-Olivia-Ammann Nov 16, 2023
27fa2e5
diagnose classes
Nora-Olivia-Ammann Nov 16, 2023
9239f38
diagnose properties
Nora-Olivia-Ammann Nov 16, 2023
167213b
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 16, 2023
122a288
Merge branch 'wip/dev-2921-check-consistency-with-ontology' of https:…
Nora-Olivia-Ammann Nov 16, 2023
4dfc26c
Create ontology_diagnose_models.py
Nora-Olivia-Ammann Nov 16, 2023
6161fb0
implement ontology diagnose tools
Nora-Olivia-Ammann Nov 16, 2023
e6da90c
remove class Ontology
Nora-Olivia-Ammann Nov 16, 2023
f6288c4
Update test_check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 16, 2023
c621a1f
Update ontology_client.py
Nora-Olivia-Ammann Nov 16, 2023
8a5f983
renaming OntoDiagnoseTool
Nora-Olivia-Ammann Nov 16, 2023
40e26be
Test OntoCheckTool
Nora-Olivia-Ammann Nov 16, 2023
0a7289b
Update ontology_diagnose_models.py
Nora-Olivia-Ammann Nov 16, 2023
2805722
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 16, 2023
752b6c0
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 16, 2023
5971b8d
Renaming and moving files.
Nora-Olivia-Ammann Nov 17, 2023
be51173
Update ontology_client.py
Nora-Olivia-Ammann Nov 17, 2023
6c40074
Moving Functions
Nora-Olivia-Ammann Nov 17, 2023
be57758
reformat ontology client
Nora-Olivia-Ammann Nov 17, 2023
eb574eb
Update ontology_client.py
Nora-Olivia-Ammann Nov 17, 2023
45452ee
reformatting test file
Nora-Olivia-Ammann Nov 17, 2023
f1183af
Update ontology diagnose models
Nora-Olivia-Ammann Nov 17, 2023
f248781
add pylint ignore
Nora-Olivia-Ammann Nov 20, 2023
db0223c
update check consistency with ontology including tests
Nora-Olivia-Ammann Nov 20, 2023
9c86f30
ontology_client add docstring
Nora-Olivia-Ammann Nov 20, 2023
ca91645
update ontology_diagnose_models
Nora-Olivia-Ammann Nov 20, 2023
bca1708
Update ontology_diagnose_models.py
Nora-Olivia-Ammann Nov 20, 2023
73a7e84
Update test_check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 20, 2023
8052949
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 20, 2023
042207e
fix save_path
Nora-Olivia-Ammann Nov 20, 2023
e8748c5
add check consistency to xmlupload
Nora-Olivia-Ammann Nov 20, 2023
74c94b8
Update check_consistency_with_ontology.py
Nora-Olivia-Ammann Nov 21, 2023
408736e
linting
Nora-Olivia-Ammann Nov 21, 2023
4d9d543
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 21, 2023
0797d12
Update ontology_diagnose_models.py
Nora-Olivia-Ammann Nov 21, 2023
e2469af
Update ontology_client.py
Nora-Olivia-Ammann Nov 21, 2023
0599e2e
Update poetry.lock
Nora-Olivia-Ammann Nov 21, 2023
e97b374
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 22, 2023
3be4842
Restructure prop/cls extraction and problem diagnosis
Nora-Olivia-Ammann Nov 22, 2023
11150a4
update the error message delivery
Nora-Olivia-Ammann Nov 22, 2023
3ff90e2
fix mypy error
Nora-Olivia-Ammann Nov 22, 2023
96c4e24
update regex
Nora-Olivia-Ammann Nov 22, 2023
6334a43
change typing
Nora-Olivia-Ammann Nov 22, 2023
4fff9e9
cleaning
Nora-Olivia-Ammann Nov 22, 2023
6beba4b
Update ontology_diagnose_models.py
Nora-Olivia-Ammann Nov 22, 2023
5bea5a1
clean up
Nora-Olivia-Ammann Nov 22, 2023
8bd44a9
move away from error
Nora-Olivia-Ammann Nov 22, 2023
34c5213
Update test_xmlupload.py
Nora-Olivia-Ammann Nov 23, 2023
00ac69f
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 27, 2023
2ccbc87
Merge branch 'main' into wip/dev-2921-check-consistency-with-ontology
Nora-Olivia-Ammann Nov 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ disable = [
"no-else-raise", # would be less readable
"logging-fstring-interpolation", # would be less readable
"duplicate-code", # too many false positives
"inconsistent-return-statements",# mypy can handle this better than pylint
"invalid-name", # TODO: activate this
"too-many-arguments", # TODO: activate this
"too-many-branches", # TODO: activate this
Expand Down
123 changes: 123 additions & 0 deletions src/dsp_tools/commands/xmlupload/check_consistency_with_ontology.py
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# sourcery skip: use-fstring-for-concatenation
import regex
from lxml import etree
from regex import Pattern

from dsp_tools.commands.xmlupload.models.ontology_diagnose_models import InvalidOntologyElements, OntoCheckInformation
from dsp_tools.commands.xmlupload.ontology_client import OntologyClientLive
from dsp_tools.models.exceptions import BaseError

defaultOntologyColon: Pattern[str] = regex.compile(r"^:\w+$")
knoraUndeclared: Pattern[str] = regex.compile(r"^\w+$")
genericPrefixedOntology: Pattern[str] = regex.compile(r"^[A-Za-z]+-?[A-Za-z]+:\w+$")
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved


def do_xml_consistency_check(onto_client: OntologyClientLive, root: etree._Element) -> None:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
"""
This function takes an OntologyClient and the root of an XML.
It retrieves the ontologies from the server.
It iterates over the root.
If it finds any invalid properties or classes, they are printed out and a UserError is raised.

Args:
onto_client: client for the ontology retrieval
root: root of the XML

Raises:
UserError: if there are any invalid properties or classes
"""
onto_check_info = OntoCheckInformation(
default_ontology_prefix=onto_client.default_ontology,
onto_lookup=onto_client.get_all_ontologies_from_server(),
save_location=onto_client.save_location,
)
classes, properties = _get_all_classes_and_properties(root)
_find_problems_in_classes_and_properties(classes, properties, onto_check_info)


def _get_all_classes_and_properties(root: etree._Element) -> tuple[list[list[str]], list[list[str]]]:
classes = _get_all_class_types_and_ids(root)
properties = []
for resource in root.iterchildren(tag="resource"):
properties.extend(_get_all_property_names_and_resource_ids(resource))
return classes, properties


def _get_all_class_types_and_ids(root: etree._Element) -> list[list[str]]:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
return [[resource.attrib["id"], resource.attrib["restype"]] for resource in root.iterchildren(tag="resource")]
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved


def _get_all_property_names_and_resource_ids(resource: etree._Element) -> list[list[str]]:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
return [[resource.attrib["id"], prop.attrib["name"]] for prop in resource.iterchildren() if prop.tag != "bitstream"]
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved


def _find_problems_in_classes_and_properties(
classes: list[list[str]], properties: list[list[str]], onto_check_info: OntoCheckInformation
) -> None:
class_problems = _diagnose_all_classes(classes, onto_check_info)
property_problems = _diagnose_all_properties(properties, onto_check_info)
if not class_problems and not property_problems:
return None
problems = InvalidOntologyElements(
save_path=onto_check_info.save_location, classes=class_problems, properties=property_problems
)
problems.execute_problem_protocol()


def _diagnose_all_classes(classes: list[list[str]], onto_check_info: OntoCheckInformation) -> list[list[str]]:
problem_list = []
for cls_info in classes:
if problem := _diagnose_class(cls_info, onto_check_info):
problem_list.append(problem)
return problem_list


def _diagnose_class(class_info: list[str], onto_check_info: OntoCheckInformation) -> list[str] | None:
try:
prefix, cls_ = _get_prefix_and_prop_cls_identifier(class_info[1], onto_check_info.default_ontology_prefix)
except BaseError:
class_info.append("Resource type does not follow a known ontology pattern")
return class_info
try:
if cls_ not in onto_check_info.onto_lookup[prefix].classes:
class_info.append("Invalid Class Type")
return class_info
return None
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
except KeyError:
class_info.append("Unknown ontology prefix")
return class_info
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved


def _diagnose_all_properties(properties: list[list[str]], onto_check_info: OntoCheckInformation) -> list[list[str]]:
problem_list = []
for prop_info in properties:
if problem := _diagnose_properties(prop_info, onto_check_info):
problem_list.append(problem)
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
return problem_list


def _diagnose_properties(prop_info: list[str], onto_check_info: OntoCheckInformation) -> list[str] | None:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
try:
prefix, prop = _get_prefix_and_prop_cls_identifier(prop_info[1], onto_check_info.default_ontology_prefix)
except BaseError:
prop_info.append("Property name does not follow a known ontology pattern")
return prop_info
try:
if prop not in onto_check_info.onto_lookup[prefix].properties:
prop_info.append("Invalid Property")
return prop_info
return None
except KeyError:
prop_info.append("Unknown ontology prefix")
return prop_info


def _get_prefix_and_prop_cls_identifier(prop_cls: str, default_ontology_prefix: str) -> tuple[str, ...]:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
if defaultOntologyColon.match(prop_cls):
return default_ontology_prefix, prop_cls.lstrip(":")
elif knoraUndeclared.match(prop_cls):
return "knora-api", prop_cls
elif genericPrefixedOntology.match(prop_cls):
return tuple(prop_cls.split(":"))
else:
raise BaseError(f"The input property or class: '{prop_cls}' does not follow a known ontology pattern.")
102 changes: 102 additions & 0 deletions src/dsp_tools/commands/xmlupload/models/ontology_diagnose_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# sourcery skip: use-fstring-for-concatenation

from dataclasses import dataclass, field
from pathlib import Path

import pandas as pd

from dsp_tools.models.exceptions import UserError


@dataclass(frozen=True)
class Ontology:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
"""This class saves the properties and the classes from an ontology."""

classes: list[str] = field(default_factory=list)
properties: list[str] = field(default_factory=list)


@dataclass
class OntoCheckInformation:
"""This class saves information that is needed to check if consistency with the ontology."""
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved

default_ontology_prefix: str
onto_lookup: dict[str, Ontology]
save_location: Path
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved


@dataclass(frozen=True)
class InvalidOntologyElements:
"""This class saves and prints out the information regarding ontology classes and properties
that are in the XML but not the ontology."""

save_path: Path
classes: list[list[str]] = field(default_factory=list)
properties: list[list[str]] = field(default_factory=list)

def execute_problem_protocol(self) -> None:
"""
This function is executed if there are any elements in properties or classes.
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
If there are more than 100 entries combined, then the result is also saved as an excel.

Raises:
UserError: If properties or classes have any entries.
"""
extra_separator = "\n\n----------------------------\n\n"
msg = "Some property and or class type(s) used in the XML are unknown:" + extra_separator
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
cls_msg = self._print_problem_string_cls()
if cls_msg:
msg += cls_msg + extra_separator
prop_msg = self._print_problem_string_props()
if prop_msg:
msg += prop_msg
if len(self.classes) + len(self.properties) > 50:
ex_name = "InvalidOntologyElements_in_XML.xlsx"
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
self._get_and_save_problems_as_excel(ex_name)
msg += extra_separator + f"\nAn excel: '{ex_name}' was saved at '{self.save_path}' listing the problems."
raise UserError(msg)
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved

def _get_and_save_problems_as_excel(self, excel_name: str) -> None:
df = self._get_problems_as_df()
df.to_excel(excel_writer=Path(self.save_path, excel_name), sheet_name=" ", index=False)

def _get_problems_as_df(self) -> pd.DataFrame:
problems = [
{
"resource id": x[0],
"problematic type": x[1],
"problem": x[2],
}
for x in self.classes
]
problems.extend(
[
{
"resource id": x[0],
"problematic type": x[1],
"problem": x[2],
}
for x in self.properties
]
)
return pd.DataFrame.from_records(problems)

def _print_problem_string_cls(self) -> str:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
if self.classes:
separator = "\n----------------------------\n"
problems = [
f"\tResource ID: '{x[0]}'\n\tResource Type: '{x[1]}'\n\tProblem: '{x[2]}'" for x in self.classes
]
return "The following resource(s) have an invalid resource type:\n\n" + separator.join(problems)
else:
return ""

def _print_problem_string_props(self) -> str:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
if self.properties:
separator = "\n----------------------------\n"
problems = [
f"\tResource ID: '{x[0]}'\n\tProperty Name: '{x[1]}'\n\tProblem: '{x[2]}'" for x in self.properties
]
return "The following resource(s) have invalid property type(s):\n\n" + separator.join(problems)
else:
return ""
133 changes: 133 additions & 0 deletions src/dsp_tools/commands/xmlupload/ontology_client.py
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Protocol

from dsp_tools.commands.xmlupload.models.ontology_diagnose_models import Ontology
from dsp_tools.models.exceptions import BaseError, UserError
from dsp_tools.utils.connection import Connection
from dsp_tools.utils.create_logger import get_logger
from dsp_tools.utils.shared import try_network_action

# pylint: disable=too-few-public-methods

logger = get_logger(__name__)


@dataclass
class OntologyClient(Protocol):
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
"""Interface (protocol) for ontology-related requests to the DSP-API."""

con: Connection
shortcode: str
default_ontology: str
save_location: Path
ontology_names: list[str] = field(default_factory=list)

def get_all_ontologies_from_server(self) -> dict[str, Ontology]:
"""Get all the ontologies for a project and the knora-api ontology from the server."""


@dataclass
class OntologyClientLive:
"""Client handling ontology-related requests to the DSP-API."""

con: Connection
shortcode: str
default_ontology: str
save_location: Path
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
ontology_names: list[str] = field(default_factory=list)

def get_all_ontologies_from_server(self) -> dict[str, Ontology]:
"""
This function returns all the project and knora-api ontology that are on the server.
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved

Returns:
a dictionary with the ontology name as key and the ontology as value.

Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
"""
ontologies = self._get_all_ontology_jsons_from_server()
return {onto_name: deserialize_ontology(onto_graph) for onto_name, onto_graph in ontologies.items()}

def _get_all_ontology_jsons_from_server(self) -> dict[str, list[dict[str, Any]]]:
self._get_ontology_names_from_server()
project_ontos = {onto: self._get_ontology_from_server(onto) for onto in self.ontology_names}
project_ontos["knora-api"] = self._get_knora_api_from_server()
return project_ontos
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved

def _get_ontology_names_from_server(self) -> None:
try:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
url = f"/admin/projects/shortcode/{self.shortcode}"
res: dict[str, Any] = try_network_action(self.con.get, route=url)
except BaseError as e:
raise UserError(f"A project with shortcode {self.shortcode} could not be found on the DSP server") from e
try:
onto_iris: list[str] = res["project"]["ontologies"]
except KeyError as e:
raise BaseError(f"Unexpected response from server: {res}") from e
onto_names: list[str] = [iri.split("/")[-1] for iri in onto_iris]
self.ontology_names = onto_names

def _get_ontology_from_server(self, ontology_name: str) -> list[dict[str, Any]]:
try:
url = f"/ontology/{self.shortcode}/{ontology_name}/v2"
res: dict[str, Any] = try_network_action(self.con.get, route=url)
except BaseError:
raise BaseError(
f"Ontologies for project {self.shortcode} could not be retrieved from the DSP server"
) from None
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
try:
onto_graph: list[dict[str, Any]] = res["@graph"]
except KeyError as e:
raise BaseError(f"Unexpected response from server: {res}") from e
return onto_graph

def _get_knora_api_from_server(self) -> list[dict[str, Any]]:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
url = "/ontology/knora-api/v2#"
try:
res: dict[str, Any] = try_network_action(self.con.get, route=url)
except BaseError:
raise BaseError("Knora-api ontology could not be retrieved from the DSP server") from None
try:
onto_graph: list[dict[str, Any]] = res["@graph"]
except KeyError as e:
raise BaseError(f"Unexpected response from server when retrieving knora-api ontology: {res}") from e
return onto_graph


def deserialize_ontology(onto_graph: list[dict[str, Any]]) -> Ontology:
"""
This function takes an ontology graph from the DSP-API.
It extracts the classes and properties.
And saves them in an instance of the class Ontology.

Args:
onto_graph: graph from DSP-API

Returns:
Ontology instance with the classes and properties
"""
classes = _get_all_cleaned_classes_from_graph(onto_graph)
properties = _get_all_cleaned_properties_from_graph(onto_graph)
return Ontology(classes, properties)


def _get_all_cleaned_classes_from_graph(onto_graph: list[dict[str, Any]]) -> list[str]:
classes = _get_all_classes_from_graph(onto_graph)
return _remove_prefixes(classes)


def _get_all_classes_from_graph(onto_graph: list[dict[str, Any]]) -> list[str]:
return [elem["@id"] for elem in onto_graph if elem.get("knora-api:isResourceClass")]
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved


def _get_all_cleaned_properties_from_graph(onto_graph: list[dict[str, Any]]) -> list[str]:
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
props = _get_all_properties_from_graph(onto_graph)
return _remove_prefixes(props)


def _get_all_properties_from_graph(onto_graph: list[dict[str, Any]]) -> list[str]:
return [elem["@id"] for elem in onto_graph if not elem.get("knora-api:isResourceClass")]


def _remove_prefixes(ontology_elements: list[str]) -> list[str]:
return [x.split(":")[1] for x in ontology_elements]
7 changes: 7 additions & 0 deletions src/dsp_tools/commands/xmlupload/xmlupload.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@

from lxml import etree

from dsp_tools.commands.xmlupload.check_consistency_with_ontology import do_xml_consistency_check
from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
from dsp_tools.commands.xmlupload.list_client import ListClient, ListClientLive
from dsp_tools.commands.xmlupload.models.permission import Permissions
from dsp_tools.commands.xmlupload.models.sipi import Sipi
from dsp_tools.commands.xmlupload.models.xmlpermission import XmlPermission
from dsp_tools.commands.xmlupload.models.xmlresource import BitstreamInfo, XMLResource
from dsp_tools.commands.xmlupload.ontology_client import OntologyClientLive
from dsp_tools.commands.xmlupload.project_client import ProjectClient, ProjectClientLive
from dsp_tools.commands.xmlupload.read_validate_xml_file import validate_and_parse_xml_file
from dsp_tools.commands.xmlupload.resource_create_client import ResourceCreateClient
Expand Down Expand Up @@ -84,6 +86,11 @@ def xmlupload(
con = login(server=server, user=user, password=password, dump=config.diagnostics.dump)
sipi_server = Sipi(sipi, con.get_token())

ontology_client = OntologyClientLive(
con=con, shortcode=shortcode, default_ontology=default_ontology, save_location=config.diagnostics.save_location
)
do_xml_consistency_check(onto_client=ontology_client, root=root)

resources, permissions_lookup, stash = _prepare_upload(
root=root,
con=con,
Expand Down