dsp-tools 17.0.0.post29__py3-none-any.whl → 18.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dsp-tools might be problematic. Click here for more details.
- dsp_tools/cli/args.py +13 -0
- dsp_tools/cli/call_action.py +34 -330
- dsp_tools/cli/call_action_files_only.py +74 -0
- dsp_tools/cli/call_action_with_network.py +203 -0
- dsp_tools/cli/create_parsers.py +50 -11
- dsp_tools/cli/utils.py +87 -0
- dsp_tools/clients/list_client.py +49 -0
- dsp_tools/clients/list_client_live.py +157 -0
- dsp_tools/clients/{ontology_client.py → ontology_clients.py} +17 -2
- dsp_tools/clients/{ontology_client_live.py → ontology_create_client_live.py} +2 -2
- dsp_tools/clients/ontology_get_client_live.py +65 -0
- dsp_tools/clients/project_client.py +10 -0
- dsp_tools/clients/project_client_live.py +30 -0
- dsp_tools/commands/create/create_on_server/cardinalities.py +14 -8
- dsp_tools/commands/create/create_on_server/lists.py +150 -0
- dsp_tools/commands/create/lists_only.py +45 -0
- dsp_tools/commands/create/models/input_problems.py +13 -0
- dsp_tools/commands/create/models/parsed_project.py +14 -1
- dsp_tools/commands/create/models/rdf_ontology.py +0 -7
- dsp_tools/commands/create/models/server_project_info.py +17 -3
- dsp_tools/commands/create/parsing/parse_lists.py +45 -0
- dsp_tools/commands/create/parsing/parse_project.py +23 -4
- dsp_tools/commands/project/create/project_create_all.py +17 -13
- dsp_tools/commands/project/create/project_create_default_permissions.py +8 -6
- dsp_tools/commands/project/create/project_create_ontologies.py +30 -18
- dsp_tools/commands/project/legacy_models/listnode.py +0 -30
- dsp_tools/commands/validate_data/models/api_responses.py +2 -16
- dsp_tools/commands/validate_data/prepare_data/prepare_data.py +8 -7
- dsp_tools/commands/validate_data/sparql/value_shacl.py +1 -1
- dsp_tools/error/exceptions.py +8 -0
- dsp_tools/resources/start-stack/docker-compose.yml +23 -23
- dsp_tools/utils/ansi_colors.py +2 -0
- {dsp_tools-17.0.0.post29.dist-info → dsp_tools-18.0.0.dist-info}/METADATA +1 -1
- {dsp_tools-17.0.0.post29.dist-info → dsp_tools-18.0.0.dist-info}/RECORD +36 -27
- {dsp_tools-17.0.0.post29.dist-info → dsp_tools-18.0.0.dist-info}/WHEEL +1 -1
- dsp_tools/commands/project/create/project_create_lists.py +0 -200
- dsp_tools/commands/validate_data/api_clients.py +0 -124
- {dsp_tools-17.0.0.post29.dist-info → dsp_tools-18.0.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from typing import Optional
|
|
3
|
-
from typing import Union
|
|
4
|
-
|
|
5
|
-
from loguru import logger
|
|
6
|
-
|
|
7
|
-
from dsp_tools.cli.args import ServerCredentials
|
|
8
|
-
from dsp_tools.clients.authentication_client_live import AuthenticationClientLive
|
|
9
|
-
from dsp_tools.clients.connection import Connection
|
|
10
|
-
from dsp_tools.clients.connection_live import ConnectionLive
|
|
11
|
-
from dsp_tools.commands.project.create.project_validate import validate_project
|
|
12
|
-
from dsp_tools.commands.project.legacy_models.listnode import ListNode
|
|
13
|
-
from dsp_tools.commands.project.legacy_models.project import Project
|
|
14
|
-
from dsp_tools.error.exceptions import BaseError
|
|
15
|
-
from dsp_tools.error.exceptions import InputError
|
|
16
|
-
from dsp_tools.utils.json_parsing import parse_json_input
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def create_lists_on_server(
|
|
20
|
-
lists_to_create: list[dict[str, Any]],
|
|
21
|
-
con: Connection,
|
|
22
|
-
project_remote: Project,
|
|
23
|
-
) -> tuple[dict[str, Any], bool]:
|
|
24
|
-
"""
|
|
25
|
-
Creates the "lists" section of a JSON project definition on a DSP server.
|
|
26
|
-
If a list with the same name is already existing in this project on the DSP server, this list is skipped.
|
|
27
|
-
If a node or an entire list cannot be created, an error message is printed, but the process continues.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
lists_to_create: "lists" section of a JSON project definition
|
|
31
|
-
con: connection to the DSP server
|
|
32
|
-
project_remote: representation of the project on the DSP server
|
|
33
|
-
|
|
34
|
-
Raises:
|
|
35
|
-
BaseError: if one of the lists to be created already exists on the DSP server, but it has no name
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
tuple consisting of the IRIs of the list nodes and the success status (True if everything went well)
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
overall_success = True
|
|
42
|
-
|
|
43
|
-
# retrieve existing lists
|
|
44
|
-
try:
|
|
45
|
-
existing_lists = ListNode.getAllLists(con=con, project_iri=project_remote.iri)
|
|
46
|
-
except BaseError:
|
|
47
|
-
err_msg = "Unable to retrieve existing lists on DSP server. Cannot check if your lists are already existing."
|
|
48
|
-
print(f"WARNING: {err_msg}")
|
|
49
|
-
logger.exception(err_msg)
|
|
50
|
-
existing_lists = []
|
|
51
|
-
overall_success = False
|
|
52
|
-
|
|
53
|
-
current_project_lists: dict[str, Any] = {}
|
|
54
|
-
for new_lst in lists_to_create:
|
|
55
|
-
if existing_lst := [x for x in existing_lists if x.project == project_remote.iri and x.name == new_lst["name"]]:
|
|
56
|
-
existing_list_name = existing_lst[0].name
|
|
57
|
-
if not existing_list_name:
|
|
58
|
-
raise BaseError(f"Node {existing_lst[0]} has no name.")
|
|
59
|
-
current_project_lists[existing_list_name] = {
|
|
60
|
-
"id": existing_lst[0].iri,
|
|
61
|
-
"nodes": new_lst["nodes"],
|
|
62
|
-
}
|
|
63
|
-
print(f" WARNING: List '{new_lst['name']}' already exists on the DSP server. Skipping...")
|
|
64
|
-
overall_success = False
|
|
65
|
-
continue
|
|
66
|
-
|
|
67
|
-
created_list, success = _create_list_node(con=con, project=project_remote, node=new_lst)
|
|
68
|
-
current_project_lists.update(created_list)
|
|
69
|
-
if not success:
|
|
70
|
-
overall_success = False
|
|
71
|
-
print(f" Created list '{new_lst['name']}'.")
|
|
72
|
-
|
|
73
|
-
return current_project_lists, overall_success
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _create_list_node(
|
|
77
|
-
con: Connection,
|
|
78
|
-
project: Project,
|
|
79
|
-
node: dict[str, Any],
|
|
80
|
-
parent_node: Optional[ListNode] = None,
|
|
81
|
-
) -> tuple[dict[str, Any], bool]:
|
|
82
|
-
"""
|
|
83
|
-
Creates a list node on the DSP server, recursively scanning through all its subnodes, creating them as well.
|
|
84
|
-
If a node cannot be created, an error message is printed, but the process continues.
|
|
85
|
-
|
|
86
|
-
Args:
|
|
87
|
-
con: connection to the DSP server
|
|
88
|
-
project: project that holds the list where this node should be added to
|
|
89
|
-
node: the node to be created
|
|
90
|
-
parent_node: parent node of the node to be created (optional)
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
Returns a tuple consisting of a dict and a bool.
|
|
94
|
-
The dict contains the IRIs of the created list nodes,
|
|
95
|
-
nested according to their hierarchy structure,
|
|
96
|
-
i.e. ``{nodename: {"id": IRI, "nodes": {...}}}``.
|
|
97
|
-
The bool is True if all nodes could be created,
|
|
98
|
-
False if any node could not be created.
|
|
99
|
-
|
|
100
|
-
Raises:
|
|
101
|
-
BaseError: if the created node has no name
|
|
102
|
-
"""
|
|
103
|
-
new_node: ListNode = ListNode(
|
|
104
|
-
con=con,
|
|
105
|
-
project=project,
|
|
106
|
-
label=node["labels"],
|
|
107
|
-
comments=node.get("comments"),
|
|
108
|
-
name=node["name"],
|
|
109
|
-
parent=parent_node,
|
|
110
|
-
)
|
|
111
|
-
try:
|
|
112
|
-
new_node = new_node.create()
|
|
113
|
-
except BaseError:
|
|
114
|
-
print(f"WARNING: Cannot create list node '{node['name']}'.")
|
|
115
|
-
logger.exception(f"Cannot create list node '{node['name']}'.")
|
|
116
|
-
return {}, False
|
|
117
|
-
|
|
118
|
-
# if node has child nodes, call the method recursively
|
|
119
|
-
if node.get("nodes"):
|
|
120
|
-
overall_success = True
|
|
121
|
-
subnode_list = []
|
|
122
|
-
for subnode in node["nodes"]:
|
|
123
|
-
created_subnode, success = _create_list_node(con=con, project=project, node=subnode, parent_node=new_node)
|
|
124
|
-
subnode_list.append(created_subnode)
|
|
125
|
-
if not success:
|
|
126
|
-
overall_success = False
|
|
127
|
-
if not new_node.name:
|
|
128
|
-
raise BaseError(f"Node {new_node} has no name.")
|
|
129
|
-
return {new_node.name: {"id": new_node.iri, "nodes": subnode_list}}, overall_success
|
|
130
|
-
else:
|
|
131
|
-
if not new_node.name:
|
|
132
|
-
raise BaseError(f"Node {new_node} has no name.")
|
|
133
|
-
return {new_node.name: {"id": new_node.iri}}, True
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def create_only_lists(
|
|
137
|
-
project_file_as_path_or_parsed: Union[str, dict[str, Any]],
|
|
138
|
-
creds: ServerCredentials,
|
|
139
|
-
) -> tuple[dict[str, Any], bool]:
|
|
140
|
-
"""
|
|
141
|
-
This function accepts a JSON project definition,
|
|
142
|
-
connects to a DSP server,
|
|
143
|
-
and uploads the "lists" section to the server.
|
|
144
|
-
|
|
145
|
-
The project must already exist on the DSP server.
|
|
146
|
-
If a list with the same name is already existing in this project on the DSP server, this list is skipped.
|
|
147
|
-
|
|
148
|
-
Args:
|
|
149
|
-
project_file_as_path_or_parsed: path to the JSON project definition, or parsed JSON object
|
|
150
|
-
creds: credentials to connect to the DSP server
|
|
151
|
-
|
|
152
|
-
Raises:
|
|
153
|
-
InputError:
|
|
154
|
-
- if the project cannot be read from the server
|
|
155
|
-
- if the connection to the DSP server cannot be established
|
|
156
|
-
|
|
157
|
-
BaseError:
|
|
158
|
-
- if the input is invalid
|
|
159
|
-
- if a problem occurred while trying to expand the Excel files
|
|
160
|
-
- if the JSON file is invalid according to the schema
|
|
161
|
-
|
|
162
|
-
Returns:
|
|
163
|
-
Returns a tuple consisting of a dict and a bool.
|
|
164
|
-
The dict contains the IRIs of the created list nodes,
|
|
165
|
-
nested according to their hierarchy structure,
|
|
166
|
-
i.e. ``{nodename: {"id": IRI, "nodes": {...}}}``.
|
|
167
|
-
If there are no lists in the project definition,
|
|
168
|
-
an empty dictionary is returned.
|
|
169
|
-
The bool indicates if everything went smoothly during the process.
|
|
170
|
-
If a warning or error occurred (e.g. one of the lists already exists,
|
|
171
|
-
or one of the nodes could not be created),
|
|
172
|
-
it is False.
|
|
173
|
-
"""
|
|
174
|
-
project_definition = parse_json_input(project_file_as_path_or_parsed=project_file_as_path_or_parsed)
|
|
175
|
-
if not project_definition.get("project", {}).get("lists"):
|
|
176
|
-
return {}, True
|
|
177
|
-
validate_project(project_definition)
|
|
178
|
-
print("JSON project file is syntactically correct and passed validation.")
|
|
179
|
-
|
|
180
|
-
auth = AuthenticationClientLive(creds.server, creds.user, creds.password)
|
|
181
|
-
con = ConnectionLive(creds.server, auth)
|
|
182
|
-
|
|
183
|
-
# retrieve the project
|
|
184
|
-
shortcode = project_definition["project"]["shortcode"]
|
|
185
|
-
project_local = Project(con=con, shortcode=shortcode)
|
|
186
|
-
try:
|
|
187
|
-
project_remote = project_local.read()
|
|
188
|
-
except BaseError:
|
|
189
|
-
err_msg = f"Unable to create the lists: The project {shortcode} cannot be found on the DSP server."
|
|
190
|
-
logger.exception(err_msg)
|
|
191
|
-
raise InputError(err_msg) from None
|
|
192
|
-
|
|
193
|
-
# create new lists
|
|
194
|
-
current_project_lists, success = create_lists_on_server(
|
|
195
|
-
lists_to_create=project_definition["project"]["lists"],
|
|
196
|
-
con=con,
|
|
197
|
-
project_remote=project_remote,
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
return current_project_lists, success
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Any
|
|
3
|
-
from typing import cast
|
|
4
|
-
from urllib.parse import quote_plus
|
|
5
|
-
|
|
6
|
-
import requests
|
|
7
|
-
|
|
8
|
-
from dsp_tools.commands.validate_data.models.api_responses import OneList
|
|
9
|
-
from dsp_tools.commands.validate_data.models.api_responses import OneNode
|
|
10
|
-
from dsp_tools.error.exceptions import InternalError
|
|
11
|
-
from dsp_tools.utils.request_utils import RequestParameters
|
|
12
|
-
from dsp_tools.utils.request_utils import log_request
|
|
13
|
-
from dsp_tools.utils.request_utils import log_response
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass
|
|
17
|
-
class OntologyClient:
|
|
18
|
-
api_url: str
|
|
19
|
-
shortcode: str
|
|
20
|
-
|
|
21
|
-
def get_knora_api(self) -> str:
|
|
22
|
-
url = f"{self.api_url}/ontology/knora-api/v2#"
|
|
23
|
-
headers = {"Accept": "text/turtle"}
|
|
24
|
-
timeout = 60
|
|
25
|
-
log_request(RequestParameters("GET", url, timeout=timeout, headers=headers))
|
|
26
|
-
response = requests.get(url=url, headers=headers, timeout=timeout)
|
|
27
|
-
log_response(response, include_response_content=False)
|
|
28
|
-
if not response.ok:
|
|
29
|
-
raise InternalError(f"Failed Request: {response.status_code} {response.text}")
|
|
30
|
-
return response.text
|
|
31
|
-
|
|
32
|
-
def get_ontologies(self) -> tuple[list[str], list[str]]:
|
|
33
|
-
"""
|
|
34
|
-
Returns a list of project ontologies as a string in turtle format.
|
|
35
|
-
And a list of the ontology IRIs
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
list of ontologies and IRIs
|
|
39
|
-
"""
|
|
40
|
-
ontology_iris = self._get_ontology_iris()
|
|
41
|
-
ontologies = [self._get_one_ontology(x) for x in ontology_iris]
|
|
42
|
-
return ontologies, ontology_iris
|
|
43
|
-
|
|
44
|
-
def _get_ontology_iris(self) -> list[str]:
|
|
45
|
-
url = f"{self.api_url}/admin/projects/shortcode/{self.shortcode}"
|
|
46
|
-
timeout = 10
|
|
47
|
-
log_request(RequestParameters("GET", url, timeout=timeout))
|
|
48
|
-
response = requests.get(url=url, timeout=timeout)
|
|
49
|
-
log_response(response)
|
|
50
|
-
if not response.ok:
|
|
51
|
-
raise InternalError(f"Failed Request: {response.status_code} {response.text}")
|
|
52
|
-
response_json = cast(dict[str, Any], response.json())
|
|
53
|
-
if not (ontos := response_json.get("project", {}).get("ontologies")):
|
|
54
|
-
raise InternalError(f"The response from the API does not contain any ontologies.\nResponse:{response.text}")
|
|
55
|
-
output = cast(list[str], ontos)
|
|
56
|
-
return output
|
|
57
|
-
|
|
58
|
-
def _get_one_ontology(self, ontology_iri: str) -> str:
|
|
59
|
-
url = ontology_iri
|
|
60
|
-
headers = {"Accept": "text/turtle"}
|
|
61
|
-
timeout = 30
|
|
62
|
-
log_request(RequestParameters("GET", url, timeout=timeout, headers=headers))
|
|
63
|
-
response = requests.get(url=url, headers=headers, timeout=timeout)
|
|
64
|
-
log_response(response, include_response_content=False)
|
|
65
|
-
if not response.ok:
|
|
66
|
-
raise InternalError(f"Failed Request: {response.status_code} {response.text}")
|
|
67
|
-
return response.text
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@dataclass
|
|
71
|
-
class ListClient:
|
|
72
|
-
"""Client to request and reformat the lists of a project."""
|
|
73
|
-
|
|
74
|
-
api_url: str
|
|
75
|
-
shortcode: str
|
|
76
|
-
|
|
77
|
-
def get_lists(self) -> list[OneList]:
|
|
78
|
-
list_json = self._get_all_list_iris()
|
|
79
|
-
all_iris = self._extract_list_iris(list_json)
|
|
80
|
-
all_lists = [self._get_one_list(iri) for iri in all_iris]
|
|
81
|
-
return [self._reformat_one_list(lst) for lst in all_lists]
|
|
82
|
-
|
|
83
|
-
def _get_all_list_iris(self) -> dict[str, Any]:
|
|
84
|
-
url = f"{self.api_url}/admin/lists?projectShortcode={self.shortcode}"
|
|
85
|
-
timeout = 10
|
|
86
|
-
log_request(RequestParameters("GET", url, timeout))
|
|
87
|
-
response = requests.get(url=url, timeout=timeout)
|
|
88
|
-
log_response(response)
|
|
89
|
-
if not response.ok:
|
|
90
|
-
raise InternalError(f"Failed Request: {response.status_code} {response.text}")
|
|
91
|
-
json_response = cast(dict[str, Any], response.json())
|
|
92
|
-
return json_response
|
|
93
|
-
|
|
94
|
-
def _extract_list_iris(self, response_json: dict[str, Any]) -> list[str]:
|
|
95
|
-
return [x["id"] for x in response_json["lists"]]
|
|
96
|
-
|
|
97
|
-
def _get_one_list(self, list_iri: str) -> dict[str, Any]:
|
|
98
|
-
encoded_list_iri = quote_plus(list_iri)
|
|
99
|
-
url = f"{self.api_url}/admin/lists/{encoded_list_iri}"
|
|
100
|
-
timeout = 30
|
|
101
|
-
log_request(RequestParameters("GET", url, timeout))
|
|
102
|
-
response = requests.get(url=url, timeout=timeout)
|
|
103
|
-
log_response(response, include_response_content=False)
|
|
104
|
-
if not response.ok:
|
|
105
|
-
raise InternalError(f"Failed Request: {response.status_code} {response.text}")
|
|
106
|
-
response_json = cast(dict[str, Any], response.json())
|
|
107
|
-
return response_json
|
|
108
|
-
|
|
109
|
-
def _reformat_one_list(self, response_json: dict[str, Any]) -> OneList:
|
|
110
|
-
list_name = response_json["list"]["listinfo"]["name"]
|
|
111
|
-
list_id = response_json["list"]["listinfo"]["id"]
|
|
112
|
-
nodes = response_json["list"]["children"]
|
|
113
|
-
all_nodes = []
|
|
114
|
-
for child in nodes:
|
|
115
|
-
all_nodes.append(OneNode(child["name"], child["id"]))
|
|
116
|
-
if node_child := child.get("children"):
|
|
117
|
-
self._reformat_children(node_child, all_nodes)
|
|
118
|
-
return OneList(list_iri=list_id, list_name=list_name, nodes=all_nodes)
|
|
119
|
-
|
|
120
|
-
def _reformat_children(self, list_child: list[dict[str, Any]], current_nodes: list[OneNode]) -> None:
|
|
121
|
-
for child in list_child:
|
|
122
|
-
current_nodes.append(OneNode(child["name"], child["id"]))
|
|
123
|
-
if grand_child := child.get("children"):
|
|
124
|
-
self._reformat_children(grand_child, current_nodes)
|
|
File without changes
|