dsp-tools 17.0.0.post26__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.

Files changed (46) hide show
  1. dsp_tools/cli/args.py +14 -0
  2. dsp_tools/cli/call_action.py +34 -327
  3. dsp_tools/cli/call_action_files_only.py +74 -0
  4. dsp_tools/cli/call_action_with_network.py +203 -0
  5. dsp_tools/cli/create_parsers.py +71 -11
  6. dsp_tools/cli/utils.py +87 -0
  7. dsp_tools/clients/list_client.py +49 -0
  8. dsp_tools/clients/list_client_live.py +157 -0
  9. dsp_tools/clients/metadata_client.py +4 -4
  10. dsp_tools/clients/metadata_client_live.py +6 -5
  11. dsp_tools/clients/{ontology_client.py → ontology_clients.py} +17 -2
  12. dsp_tools/clients/{ontology_client_live.py → ontology_create_client_live.py} +2 -2
  13. dsp_tools/clients/ontology_get_client_live.py +65 -0
  14. dsp_tools/clients/project_client.py +10 -0
  15. dsp_tools/clients/project_client_live.py +30 -0
  16. dsp_tools/commands/create/create_on_server/cardinalities.py +14 -8
  17. dsp_tools/commands/create/create_on_server/lists.py +150 -0
  18. dsp_tools/commands/create/lists_only.py +45 -0
  19. dsp_tools/commands/create/models/input_problems.py +13 -0
  20. dsp_tools/commands/create/models/parsed_project.py +14 -1
  21. dsp_tools/commands/create/models/rdf_ontology.py +0 -7
  22. dsp_tools/commands/create/models/server_project_info.py +17 -3
  23. dsp_tools/commands/create/parsing/parse_lists.py +45 -0
  24. dsp_tools/commands/create/parsing/parse_project.py +23 -4
  25. dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +4 -0
  26. dsp_tools/commands/project/create/project_create_all.py +17 -13
  27. dsp_tools/commands/project/create/project_create_default_permissions.py +8 -6
  28. dsp_tools/commands/project/create/project_create_ontologies.py +30 -18
  29. dsp_tools/commands/project/legacy_models/listnode.py +0 -30
  30. dsp_tools/commands/validate_data/models/api_responses.py +2 -16
  31. dsp_tools/commands/validate_data/models/input_problems.py +1 -0
  32. dsp_tools/commands/validate_data/prepare_data/prepare_data.py +24 -16
  33. dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +53 -23
  34. dsp_tools/commands/validate_data/sparql/value_shacl.py +1 -1
  35. dsp_tools/commands/validate_data/validate_data.py +11 -3
  36. dsp_tools/commands/xmlupload/upload_config.py +1 -0
  37. dsp_tools/commands/xmlupload/xmlupload.py +1 -0
  38. dsp_tools/error/exceptions.py +8 -0
  39. dsp_tools/resources/start-stack/docker-compose.yml +23 -23
  40. dsp_tools/utils/ansi_colors.py +2 -0
  41. {dsp_tools-17.0.0.post26.dist-info → dsp_tools-18.0.0.dist-info}/METADATA +1 -1
  42. {dsp_tools-17.0.0.post26.dist-info → dsp_tools-18.0.0.dist-info}/RECORD +44 -35
  43. {dsp_tools-17.0.0.post26.dist-info → dsp_tools-18.0.0.dist-info}/WHEEL +1 -1
  44. dsp_tools/commands/project/create/project_create_lists.py +0 -200
  45. dsp_tools/commands/validate_data/api_clients.py +0 -124
  46. {dsp_tools-17.0.0.post26.dist-info → dsp_tools-18.0.0.dist-info}/entry_points.txt +0 -0
@@ -15,12 +15,13 @@ from dsp_tools.clients.authentication_client_live import AuthenticationClientLiv
15
15
  from dsp_tools.clients.connection import Connection
16
16
  from dsp_tools.clients.connection_live import ConnectionLive
17
17
  from dsp_tools.commands.create.communicate_problems import print_problem_collection
18
+ from dsp_tools.commands.create.create_on_server.lists import create_lists
19
+ from dsp_tools.commands.create.create_on_server.lists import get_existing_lists_on_server
18
20
  from dsp_tools.commands.create.models.parsed_project import ParsedProject
19
21
  from dsp_tools.commands.create.models.server_project_info import ProjectIriLookup
20
22
  from dsp_tools.commands.create.parsing.parse_project import parse_project
21
23
  from dsp_tools.commands.project.create.parse_project import parse_project_json
22
24
  from dsp_tools.commands.project.create.project_create_default_permissions import create_default_permissions
23
- from dsp_tools.commands.project.create.project_create_lists import create_lists_on_server
24
25
  from dsp_tools.commands.project.create.project_create_ontologies import create_ontologies
25
26
  from dsp_tools.commands.project.legacy_models.context import Context
26
27
  from dsp_tools.commands.project.legacy_models.group import Group
@@ -33,6 +34,8 @@ from dsp_tools.error.exceptions import InputError
33
34
  from dsp_tools.error.exceptions import InvalidInputError
34
35
  from dsp_tools.error.exceptions import PermanentConnectionError
35
36
  from dsp_tools.legacy_models.langstring import LangString
37
+ from dsp_tools.utils.ansi_colors import BOLD
38
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
36
39
  from dsp_tools.utils.json_parsing import parse_json_input
37
40
 
38
41
  load_dotenv()
@@ -89,8 +92,8 @@ def create_project( # noqa: PLR0915,PLR0912 (too many statements & branches)
89
92
  con = ConnectionLive(creds.server, auth)
90
93
 
91
94
  # create project on DSP server
92
- info_str = f"Create project '{legacy_project.metadata.shortname}' ({legacy_project.metadata.shortcode})..."
93
- print(info_str)
95
+ info_str = f"Create project '{legacy_project.metadata.shortname}' ({legacy_project.metadata.shortcode}):"
96
+ print(BOLD + info_str + RESET_TO_DEFAULT)
94
97
  logger.info(info_str)
95
98
  project_remote, success = _create_project_on_server(
96
99
  project_definition=legacy_project.metadata,
@@ -102,17 +105,18 @@ def create_project( # noqa: PLR0915,PLR0912 (too many statements & branches)
102
105
  overall_success = False
103
106
 
104
107
  # create the lists
105
- names_and_iris_of_list_nodes: dict[str, Any] = {}
106
- if legacy_project.lists:
107
- print("Create lists...")
108
- logger.info("Create lists...")
109
- names_and_iris_of_list_nodes, success = create_lists_on_server(
110
- lists_to_create=legacy_project.lists,
111
- con=con,
112
- project_remote=project_remote,
108
+ if parsed_project.lists:
109
+ list_name_2_iri, list_problems = create_lists(
110
+ parsed_lists=parsed_project.lists,
111
+ shortcode=parsed_project.project_metadata.shortcode,
112
+ auth=auth,
113
+ project_iri=project_iri,
113
114
  )
114
- if not success:
115
+ if list_problems:
115
116
  overall_success = False
117
+ print_problem_collection(list_problems)
118
+ else:
119
+ list_name_2_iri = get_existing_lists_on_server(parsed_project.project_metadata.shortcode, auth)
116
120
 
117
121
  # create the groups
118
122
  current_project_groups: dict[str, Group] = {}
@@ -146,7 +150,7 @@ def create_project( # noqa: PLR0915,PLR0912 (too many statements & branches)
146
150
  con=con,
147
151
  context=context,
148
152
  knora_api_prefix=knora_api_prefix,
149
- names_and_iris_of_list_nodes=names_and_iris_of_list_nodes,
153
+ list_name_2_iri=list_name_2_iri,
150
154
  ontology_definitions=legacy_project.ontologies,
151
155
  project_remote=project_remote,
152
156
  verbose=verbose,
@@ -1,6 +1,8 @@
1
1
  from loguru import logger
2
2
 
3
3
  from dsp_tools.commands.project.models.permissions_client import PermissionsClient
4
+ from dsp_tools.utils.ansi_colors import BOLD
5
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
4
6
 
5
7
  USER_IRI_PREFIX = "http://www.knora.org/ontology/knora-admin#"
6
8
 
@@ -11,23 +13,23 @@ def create_default_permissions(
11
13
  default_permissions_overrule: dict[str, str | list[str]] | None,
12
14
  shortcode: str,
13
15
  ) -> bool:
14
- logger.info("Set default permissions...")
15
- print("Set default permissions...")
16
+ print(BOLD + "Processing default permissions:" + RESET_TO_DEFAULT)
17
+ logger.info("Processing default permissions:")
16
18
  if not _delete_existing_doaps(perm_client):
17
- print("WARNING: Cannot delete the existing default permissions")
19
+ print(" WARNING: Cannot delete the existing default permissions")
18
20
  logger.warning("Cannot delete the existing default permissions")
19
21
  return False
20
22
  if not _create_new_doap(perm_client, default_permissions):
21
- print("WARNING: Cannot create default permissions")
23
+ print(" WARNING: Cannot create default permissions")
22
24
  logger.warning("Cannot create default permissions")
23
25
  return False
24
26
  if default_permissions_overrule:
25
27
  if not _create_overrules(perm_client, default_permissions_overrule, shortcode):
26
- print("WARNING: Cannot create default permissions overrules")
28
+ print(" WARNING: Cannot create default permissions overrules")
27
29
  logger.warning("Cannot create default permissions overrules")
28
30
  return False
31
+ print(" Default permissions have been set")
29
32
  logger.info("Default permissions have been set")
30
- print("Default permissions have been set")
31
33
  return True
32
34
 
33
35
 
@@ -7,11 +7,12 @@ from loguru import logger
7
7
 
8
8
  from dsp_tools.clients.authentication_client import AuthenticationClient
9
9
  from dsp_tools.clients.connection import Connection
10
- from dsp_tools.clients.ontology_client_live import OntologyClientLive
10
+ from dsp_tools.clients.ontology_create_client_live import OntologyCreateClientLive
11
11
  from dsp_tools.commands.create.communicate_problems import print_problem_collection
12
12
  from dsp_tools.commands.create.create_on_server.cardinalities import add_all_cardinalities
13
13
  from dsp_tools.commands.create.models.parsed_ontology import ParsedOntology
14
14
  from dsp_tools.commands.create.models.server_project_info import CreatedIriCollection
15
+ from dsp_tools.commands.create.models.server_project_info import ListNameToIriLookup
15
16
  from dsp_tools.commands.create.models.server_project_info import ProjectIriLookup
16
17
  from dsp_tools.commands.project.legacy_models.context import Context
17
18
  from dsp_tools.commands.project.legacy_models.ontology import Ontology
@@ -22,13 +23,15 @@ from dsp_tools.error.exceptions import BaseError
22
23
  from dsp_tools.error.exceptions import InputError
23
24
  from dsp_tools.legacy_models.datetimestamp import DateTimeStamp
24
25
  from dsp_tools.legacy_models.langstring import LangString
26
+ from dsp_tools.utils.ansi_colors import BOLD
27
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
25
28
 
26
29
 
27
30
  def create_ontologies(
28
31
  con: Connection,
29
32
  context: Context,
30
33
  knora_api_prefix: str,
31
- names_and_iris_of_list_nodes: dict[str, Any],
34
+ list_name_2_iri: ListNameToIriLookup,
32
35
  ontology_definitions: list[dict[str, Any]],
33
36
  project_remote: Project,
34
37
  verbose: bool,
@@ -45,7 +48,7 @@ def create_ontologies(
45
48
  con: Connection to the DSP server
46
49
  context: prefixes and the ontology IRIs they stand for
47
50
  knora_api_prefix: the prefix that stands for the knora-api ontology
48
- names_and_iris_of_list_nodes: IRIs of list nodes that were already created and are available on the DSP server
51
+ list_name_2_iri: IRIs of list nodes that were already created and are available on the DSP server
49
52
  ontology_definitions: the "ontologies" section of the parsed JSON project file
50
53
  project_remote: representation of the project on the DSP server
51
54
  verbose: verbose switch
@@ -61,17 +64,15 @@ def create_ontologies(
61
64
  True if everything went smoothly, False otherwise
62
65
  """
63
66
  success_collection = CreatedIriCollection()
64
- onto_client = OntologyClientLive(auth.server, auth)
67
+ onto_client = OntologyCreateClientLive(auth.server, auth)
65
68
 
66
69
  overall_success = True
67
-
68
- print("Create ontologies...")
69
- logger.info("Create ontologies...")
70
+ logger.info("Processing Ontology Section")
70
71
  try:
71
72
  project_ontologies = Ontology.getProjectOntologies(con=con, project_id=str(project_remote.iri))
72
73
  except BaseError:
73
74
  err_msg = "Unable to retrieve remote ontologies. Cannot check if your ontology already exists."
74
- print("WARNING: {err_msg}")
75
+ print(f" WARNING: {err_msg}")
75
76
  logger.exception(err_msg)
76
77
  project_ontologies = []
77
78
 
@@ -114,7 +115,7 @@ def create_ontologies(
114
115
  onto_name=ontology_definition["name"],
115
116
  property_definitions=ontology_definition.get("properties", []),
116
117
  ontology_remote=ontology_remote,
117
- names_and_iris_of_list_nodes=names_and_iris_of_list_nodes,
118
+ list_name_2_iri=list_name_2_iri,
118
119
  con=con,
119
120
  last_modification_date=last_modification_date,
120
121
  knora_api_prefix=knora_api_prefix,
@@ -125,7 +126,7 @@ def create_ontologies(
125
126
  if not success:
126
127
  overall_success = False
127
128
 
128
- print("Add cardinalities to resource classes...")
129
+ print(BOLD + "Processing Cardinalities:" + RESET_TO_DEFAULT)
129
130
  problems = add_all_cardinalities(
130
131
  ontologies=parsed_ontologies,
131
132
  project_iri_lookup=project_iri_lookup,
@@ -173,12 +174,12 @@ def _create_ontology(
173
174
  # skip if it already exists on the DSP server
174
175
  if onto_name in [onto.name for onto in project_ontologies]:
175
176
  err_msg = f"Ontology '{onto_name}' already exists on the DSP server. Skipping..."
176
- print(f" WARNING: {err_msg}")
177
+ print(f"WARNING: {err_msg}")
177
178
  logger.warning(err_msg)
178
179
  return None
179
180
 
180
- print(f"Create ontology '{onto_name}'...")
181
- logger.info(f"Create ontology '{onto_name}'...")
181
+ print(BOLD + f"Processing ontology '{onto_name}':" + RESET_TO_DEFAULT)
182
+ logger.info(f"Processing ontology '{onto_name}'")
182
183
  ontology_local = Ontology(
183
184
  con=con,
184
185
  project=project_remote,
@@ -308,11 +309,11 @@ def _sort_resources(
308
309
  return sorted_resources
309
310
 
310
311
 
311
- def _add_property_classes_to_remote_ontology(
312
+ def _add_property_classes_to_remote_ontology( # noqa: PLR0912
312
313
  onto_name: str,
313
314
  property_definitions: list[dict[str, Any]],
314
315
  ontology_remote: Ontology,
315
- names_and_iris_of_list_nodes: dict[str, Any],
316
+ list_name_2_iri: ListNameToIriLookup,
316
317
  con: Connection,
317
318
  last_modification_date: DateTimeStamp,
318
319
  knora_api_prefix: str,
@@ -328,7 +329,7 @@ def _add_property_classes_to_remote_ontology(
328
329
  onto_name: name of the current ontology
329
330
  property_definitions: the part of the parsed JSON project file that contains the properties of the current onto
330
331
  ontology_remote: representation of the current ontology on the DSP server
331
- names_and_iris_of_list_nodes: IRIs of list nodes that were already created and are available on the DSP server
332
+ list_name_2_iri: IRIs of list nodes that were already created and are available on the DSP server
332
333
  con: connection to the DSP server
333
334
  last_modification_date: last modification date of the ontology on the DSP server
334
335
  knora_api_prefix: the prefix that stands for the knora-api ontology
@@ -373,7 +374,18 @@ def _add_property_classes_to_remote_ontology(
373
374
  # get the gui_attributes
374
375
  gui_attributes = prop_class.get("gui_attributes")
375
376
  if gui_attributes and gui_attributes.get("hlist"):
376
- list_iri = names_and_iris_of_list_nodes[gui_attributes["hlist"]]["id"]
377
+ list_name = gui_attributes["hlist"]
378
+ list_iri = list_name_2_iri.get_iri(list_name)
379
+ if not list_iri:
380
+ err_msg = (
381
+ f"Unable to create property class '{prop_class['name']}' "
382
+ f"because the list with the name '{list_name}' does not exist on the server."
383
+ )
384
+ print(f" WARNING: {err_msg}")
385
+ logger.warning(err_msg)
386
+ overall_success = False
387
+ continue
388
+
377
389
  gui_attributes["hlist"] = f"<{list_iri}>"
378
390
 
379
391
  # create the property class
@@ -405,7 +417,7 @@ def _add_property_classes_to_remote_ontology(
405
417
  err.message,
406
418
  ):
407
419
  err_msg += f", because it refers to a class of another project: '{found.group(1)}'."
408
- print(f"WARNING: {err_msg}")
420
+ print(f" WARNING: {err_msg}")
409
421
  logger.exception(err_msg)
410
422
  overall_success = False
411
423
 
@@ -301,36 +301,6 @@ class ListNode(Model):
301
301
  rootNodeIri=rootNodeIri,
302
302
  )
303
303
 
304
- def create(self) -> ListNode:
305
- """
306
- Create a new List
307
-
308
- :return: JSON-object from DSP-API
309
- """
310
- jsonobj = self._toJsonObj_create()
311
- if self._parent:
312
- result = self._con.post(ListNode.ROUTE_SLASH + quote_plus(self._parent), jsonobj)
313
- return ListNode.fromJsonObj(self._con, result["nodeinfo"])
314
- else:
315
- result = self._con.post(ListNode.ROUTE, jsonobj)
316
- return ListNode.fromJsonObj(self._con, result["list"]["listinfo"])
317
-
318
- def _toJsonObj_create(self):
319
- tmp = {}
320
- if self._project is None:
321
- raise BaseError("There must be a project id given!")
322
- tmp["projectIri"] = self._project
323
- if self._label.isEmpty():
324
- raise BaseError("There must be a valid ListNode label!")
325
- tmp["labels"] = self._label.toJsonObj()
326
- if self._comments:
327
- tmp["comments"] = self._comments.toJsonObj()
328
- if self._name:
329
- tmp["name"] = self._name
330
- if self._parent:
331
- tmp["parentNodeIri"] = self._parent
332
- return tmp
333
-
334
304
  def read(self) -> Any:
335
305
  """
336
306
  Read a project from DSP-API
@@ -5,6 +5,8 @@ from dataclasses import dataclass
5
5
  from rdflib import Graph
6
6
  from rdflib import URIRef
7
7
 
8
+ from dsp_tools.clients.list_client import OneList
9
+
8
10
 
9
11
  @dataclass
10
12
  class SHACLValidationReport:
@@ -24,22 +26,6 @@ class ListLookup:
24
26
  lists: dict[tuple[str, str], str]
25
27
 
26
28
 
27
- @dataclass
28
- class OneList:
29
- list_iri: str
30
- list_name: str
31
- nodes: list[OneNode]
32
-
33
- def hlist(self) -> str:
34
- return f'"hlist=<{self.list_iri}>"'
35
-
36
-
37
- @dataclass
38
- class OneNode:
39
- name: str
40
- iri: str
41
-
42
-
43
29
  @dataclass
44
30
  class SHACLListInfo:
45
31
  list_iri: URIRef
@@ -114,5 +114,6 @@ class ProblemType(StrEnum):
114
114
  LINK_TARGET_TYPE_MISMATCH = "Linked Resource Type Mismatch"
115
115
  LINK_TARGET_OF_ANOTHER_PROJECT = "Linked Resource is from another project"
116
116
  LINK_TARGET_IS_IRI_OF_PROJECT = "Linked resource is an IRI of the project"
117
+ LINK_TARGET_NOT_FOUND_IN_DB = "Linked resource is an IRI and could not be found in the database."
117
118
  INEXISTENT_LINKED_RESOURCE = "Linked Resource does not exist"
118
119
  DUPLICATE_VALUE = "Your input is duplicated"
@@ -8,14 +8,15 @@ from rdflib import URIRef
8
8
 
9
9
  from dsp_tools.clients.authentication_client import AuthenticationClient
10
10
  from dsp_tools.clients.legal_info_client_live import LegalInfoClientLive
11
- from dsp_tools.clients.metadata_client import MetadataRetrieval
11
+ from dsp_tools.clients.list_client import OneList
12
+ from dsp_tools.clients.list_client_live import ListGetClientLive
13
+ from dsp_tools.clients.metadata_client import ExistingResourcesRetrieved
12
14
  from dsp_tools.clients.metadata_client_live import MetadataClientLive
13
- from dsp_tools.commands.validate_data.api_clients import ListClient
14
- from dsp_tools.commands.validate_data.api_clients import OntologyClient
15
+ from dsp_tools.clients.ontology_clients import OntologyGetClient
16
+ from dsp_tools.clients.ontology_get_client_live import OntologyGetClientLive
15
17
  from dsp_tools.commands.validate_data.models.api_responses import EnabledLicenseIris
16
18
  from dsp_tools.commands.validate_data.models.api_responses import InfoForResourceInDB
17
19
  from dsp_tools.commands.validate_data.models.api_responses import ListLookup
18
- from dsp_tools.commands.validate_data.models.api_responses import OneList
19
20
  from dsp_tools.commands.validate_data.models.api_responses import ProjectDataFromApi
20
21
  from dsp_tools.commands.validate_data.models.validation import RDFGraphs
21
22
  from dsp_tools.commands.validate_data.prepare_data.get_rdf_like_data import get_rdf_like_data
@@ -47,13 +48,16 @@ def prepare_data_for_validation_from_parsed_resource(
47
48
  permission_ids: list[str],
48
49
  auth: AuthenticationClient,
49
50
  shortcode: str,
50
- ) -> tuple[RDFGraphs, set[str], MetadataRetrieval]:
51
+ do_not_request_resource_metadata_from_db: bool,
52
+ ) -> tuple[RDFGraphs, set[str], ExistingResourcesRetrieved]:
51
53
  used_iris = {x.res_type for x in parsed_resources}
52
- proj_info, metadata_retrieval_success = _get_project_specific_information_from_api(auth, shortcode)
54
+ proj_info, existing_resources_retrieved = _get_project_specific_information_from_api(
55
+ auth, shortcode, do_not_request_resource_metadata_from_db
56
+ )
53
57
  list_lookup = _make_list_lookup(proj_info.all_lists)
54
58
  data_rdf = _make_data_graph_from_parsed_resources(parsed_resources, authorship_lookup, list_lookup)
55
59
  rdf_graphs = _create_graphs(data_rdf, shortcode, auth, proj_info, permission_ids)
56
- return rdf_graphs, used_iris, metadata_retrieval_success
60
+ return rdf_graphs, used_iris, existing_resources_retrieved
57
61
 
58
62
 
59
63
  def _make_list_lookup(project_lists: list[OneList]) -> ListLookup:
@@ -66,18 +70,22 @@ def _make_list_lookup(project_lists: list[OneList]) -> ListLookup:
66
70
 
67
71
 
68
72
  def _get_project_specific_information_from_api(
69
- auth: AuthenticationClient, shortcode: str
70
- ) -> tuple[ProjectDataFromApi, MetadataRetrieval]:
71
- list_client = ListClient(auth.server, shortcode)
72
- all_lists = list_client.get_lists()
73
+ auth: AuthenticationClient, shortcode: str, do_not_request_resource_metadata_from_db: bool
74
+ ) -> tuple[ProjectDataFromApi, ExistingResourcesRetrieved]:
75
+ list_client = ListGetClientLive(auth.server, shortcode)
76
+ all_lists = list_client.get_all_lists_and_nodes()
73
77
  enabled_licenses = _get_license_iris(shortcode, auth)
74
- retrieval_status, formatted_metadata = _get_metadata_info(auth, shortcode)
75
- return ProjectDataFromApi(all_lists, enabled_licenses, formatted_metadata), retrieval_status
78
+ if do_not_request_resource_metadata_from_db:
79
+ existing_resources_retrieved = ExistingResourcesRetrieved.FALSE
80
+ formatted_metadata: list[InfoForResourceInDB] = []
81
+ else:
82
+ existing_resources_retrieved, formatted_metadata = _get_metadata_info(auth, shortcode)
83
+ return ProjectDataFromApi(all_lists, enabled_licenses, formatted_metadata), existing_resources_retrieved
76
84
 
77
85
 
78
86
  def _get_metadata_info(
79
87
  auth: AuthenticationClient, shortcode: str
80
- ) -> tuple[MetadataRetrieval, list[InfoForResourceInDB]]:
88
+ ) -> tuple[ExistingResourcesRetrieved, list[InfoForResourceInDB]]:
81
89
  metadata_client = MetadataClientLive(auth.server, auth)
82
90
  retrieval_status, metadata = metadata_client.get_resource_metadata(shortcode)
83
91
  formatted_metadata = [InfoForResourceInDB(x["resourceIri"], x["resourceClassIri"]) for x in metadata]
@@ -100,7 +108,7 @@ def _create_graphs(
100
108
  permission_ids: list[str],
101
109
  ) -> RDFGraphs:
102
110
  logger.debug("Create all graphs.")
103
- onto_client = OntologyClient(auth.server, shortcode)
111
+ onto_client = OntologyGetClientLive(auth.server, shortcode)
104
112
  ontologies, onto_iris = _get_project_ontos(onto_client)
105
113
  knora_ttl = onto_client.get_knora_api()
106
114
  knora_api = Graph()
@@ -147,7 +155,7 @@ def _bind_prefixes_to_graph(g: Graph, project_ontos: list[str]) -> Graph:
147
155
  return g
148
156
 
149
157
 
150
- def _get_project_ontos(onto_client: OntologyClient) -> tuple[Graph, list[str]]:
158
+ def _get_project_ontos(onto_client: OntologyGetClient) -> tuple[Graph, list[str]]:
151
159
  logger.debug("Get project ontologies from server.")
152
160
  all_ontos, onto_iris = onto_client.get_ontologies()
153
161
  onto_g = Graph()
@@ -3,6 +3,7 @@ from collections import defaultdict
3
3
  import pandas as pd
4
4
 
5
5
  from dsp_tools.cli.args import ValidationSeverity
6
+ from dsp_tools.clients.metadata_client import ExistingResourcesRetrieved
6
7
  from dsp_tools.commands.validate_data.models.input_problems import AllProblems
7
8
  from dsp_tools.commands.validate_data.models.input_problems import DuplicateFileWarning
8
9
  from dsp_tools.commands.validate_data.models.input_problems import InputProblem
@@ -20,9 +21,14 @@ PROBLEM_TYPES_IGNORE_STR_ENUM_INFO = {ProblemType.GENERIC, ProblemType.FILE_VALU
20
21
 
21
22
 
22
23
  def sort_user_problems(
23
- all_problems: AllProblems, duplicate_file_warnings: DuplicateFileWarning | None, shortcode: str
24
+ all_problems: AllProblems,
25
+ duplicate_file_warnings: DuplicateFileWarning | None,
26
+ shortcode: str,
27
+ existing_resources_retrieved: ExistingResourcesRetrieved,
24
28
  ) -> SortedProblems:
25
- iris_removed, links_level_info = _separate_resource_links_to_iris_of_own_project(all_problems.problems, shortcode)
29
+ iris_removed, links_level_info = _separate_resource_links_to_iris_of_own_project(
30
+ all_problems.problems, shortcode, existing_resources_retrieved
31
+ )
26
32
  filtered_problems = _filter_out_duplicate_problems(iris_removed)
27
33
  violations, warnings, info = _separate_according_to_severity(filtered_problems)
28
34
  if duplicate_file_warnings:
@@ -47,37 +53,61 @@ def _separate_according_to_severity(
47
53
 
48
54
 
49
55
  def _separate_resource_links_to_iris_of_own_project(
50
- problems: list[InputProblem], shortcode: str
56
+ problems: list[InputProblem], shortcode: str, existing_resources_retrieved: ExistingResourcesRetrieved
51
57
  ) -> tuple[list[InputProblem], list[InputProblem]]:
52
58
  link_level_info = []
53
59
  all_others = []
54
- resource_iri_start = "http://rdfh.ch/"
55
- project_resource_iri = f"{resource_iri_start}{shortcode}/"
56
60
  for prblm in problems:
57
61
  if prblm.problem_type != ProblemType.INEXISTENT_LINKED_RESOURCE:
58
62
  all_others.append(prblm)
59
- continue
60
- if not prblm.input_value:
61
- all_others.append(prblm)
62
- elif prblm.input_value.startswith(project_resource_iri):
63
- prblm.message = (
64
- "You used an absolute IRI to reference an existing resource in the DB. "
65
- "If this resource does not exist or is not of the correct type, an xmlupload will fail."
66
- )
67
- prblm.problem_type = ProblemType.LINK_TARGET_IS_IRI_OF_PROJECT
68
- link_level_info.append(prblm)
69
- elif prblm.input_value.startswith(resource_iri_start):
70
- prblm.message = (
71
- "You used an absolute IRI to reference an existing resource of another project in the DB. "
72
- "Cross-Project resource links are not permitted."
73
- )
74
- prblm.problem_type = ProblemType.LINK_TARGET_OF_ANOTHER_PROJECT
75
- all_others.append(prblm)
76
63
  else:
77
- all_others.append(prblm)
64
+ is_violation, triaged_problem = _determined_link_value_message_and_level(
65
+ prblm, shortcode, existing_resources_retrieved
66
+ )
67
+ if is_violation:
68
+ all_others.append(triaged_problem)
69
+ else:
70
+ link_level_info.append(triaged_problem)
78
71
  return all_others, link_level_info
79
72
 
80
73
 
74
+ def _determined_link_value_message_and_level(
75
+ problem: InputProblem, shortcode: str, existing_resources_retrieved: ExistingResourcesRetrieved
76
+ ) -> tuple[bool, InputProblem]:
77
+ is_violation = True
78
+ resource_iri_start = "http://rdfh.ch/"
79
+ project_resource_iri = f"{resource_iri_start}{shortcode}/"
80
+ if not problem.input_value:
81
+ return is_violation, problem
82
+ if problem.input_value.startswith(project_resource_iri):
83
+ # case IRI and matches those of the projects itself
84
+ if existing_resources_retrieved == ExistingResourcesRetrieved.TRUE:
85
+ # if metadata was sucessfully retrieved, then the IRI is wrong
86
+ problem.problem_type = ProblemType.LINK_TARGET_NOT_FOUND_IN_DB
87
+ problem.message = (
88
+ "You used an absolute IRI to reference an existing resource in the DB. "
89
+ "We could not find a reference to this resource in the database."
90
+ )
91
+ return is_violation, problem
92
+ # if we could not retrieve the metadata, then we cannot verify if it exists or not, so it is only an info
93
+ problem.problem_type = ProblemType.LINK_TARGET_IS_IRI_OF_PROJECT
94
+ problem.message = (
95
+ "You used an absolute IRI to reference an existing resource in the DB. "
96
+ "If this resource does not exist or is not of the correct type, an xmlupload will fail."
97
+ )
98
+ return not is_violation, problem
99
+ if problem.input_value.startswith(resource_iri_start):
100
+ # case IRI, but does not contain the shortcode of the project
101
+ problem.message = (
102
+ "You used an absolute IRI to reference an existing resource of another project in the DB. "
103
+ "Cross-Project resource links are not permitted."
104
+ )
105
+ problem.problem_type = ProblemType.LINK_TARGET_OF_ANOTHER_PROJECT
106
+ return is_violation, problem
107
+ # all other cases, it is not an IRI and must be an internal ID that does not exist in the XML
108
+ return is_violation, problem
109
+
110
+
81
111
  def _filter_out_duplicate_problems(problems: list[InputProblem]) -> list[InputProblem]:
82
112
  grouped, without_res_id = _group_problems_by_resource(problems)
83
113
  filtered = without_res_id
@@ -8,7 +8,7 @@ from rdflib import Literal
8
8
  from rdflib import URIRef
9
9
  from rdflib.collection import Collection
10
10
 
11
- from dsp_tools.commands.validate_data.models.api_responses import OneList
11
+ from dsp_tools.clients.list_client import OneList
12
12
  from dsp_tools.commands.validate_data.models.api_responses import SHACLListInfo
13
13
  from dsp_tools.utils.rdflib_constants import KNORA_API
14
14
  from dsp_tools.utils.rdflib_constants import PropertyTypeAlias
@@ -10,6 +10,7 @@ from dsp_tools.cli.args import ValidateDataConfig
10
10
  from dsp_tools.cli.args import ValidationSeverity
11
11
  from dsp_tools.clients.authentication_client import AuthenticationClient
12
12
  from dsp_tools.clients.authentication_client_live import AuthenticationClientLive
13
+ from dsp_tools.clients.metadata_client import ExistingResourcesRetrieved
13
14
  from dsp_tools.commands.validate_data.models.input_problems import OntologyValidationProblem
14
15
  from dsp_tools.commands.validate_data.models.input_problems import SortedProblems
15
16
  from dsp_tools.commands.validate_data.models.input_problems import UnknownClassesInData
@@ -51,6 +52,7 @@ def validate_data(
51
52
  save_graphs: bool,
52
53
  skip_ontology_validation: bool,
53
54
  id2iri_replacement_file: str | None,
55
+ do_not_request_resource_metadata_from_db: bool,
54
56
  ) -> bool:
55
57
  """
56
58
  Takes a file and project information and validates it against the ontologies on the server.
@@ -62,6 +64,7 @@ def validate_data(
62
64
  save_graphs: if this flag is set, all the graphs will be saved in a folder
63
65
  skip_ontology_validation: skip the ontology validation
64
66
  id2iri_replacement_file: to replace internal IDs of an XML file by IRIs provided in this mapping file
67
+ do_not_request_resource_metadata_from_db: true if no metadata for existing resources should be requested
65
68
 
66
69
  Returns:
67
70
  True if no errors that impede an xmlupload were found.
@@ -78,6 +81,7 @@ def validate_data(
78
81
  ignore_duplicate_files_warning=ignore_duplicate_files_warning,
79
82
  is_on_prod_server=is_prod_like_server(creds.server),
80
83
  skip_ontology_validation=skip_ontology_validation,
84
+ do_not_request_resource_metadata_from_db=do_not_request_resource_metadata_from_db,
81
85
  )
82
86
  auth = AuthenticationClientLive(server=creds.server, email=creds.user, password=creds.password)
83
87
 
@@ -104,14 +108,17 @@ def validate_parsed_resources(
104
108
  config: ValidateDataConfig,
105
109
  auth: AuthenticationClient,
106
110
  ) -> bool:
107
- rdf_graphs, used_iris, _ = prepare_data_for_validation_from_parsed_resource(
111
+ rdf_graphs, used_iris, existing_resources_retrieved = prepare_data_for_validation_from_parsed_resource(
108
112
  parsed_resources=parsed_resources,
109
113
  authorship_lookup=authorship_lookup,
110
114
  permission_ids=permission_ids,
111
115
  auth=auth,
112
116
  shortcode=shortcode,
117
+ do_not_request_resource_metadata_from_db=config.do_not_request_resource_metadata_from_db,
118
+ )
119
+ validation_result = _validate_data(
120
+ rdf_graphs, used_iris, parsed_resources, config, shortcode, existing_resources_retrieved
113
121
  )
114
- validation_result = _validate_data(rdf_graphs, used_iris, parsed_resources, config, shortcode)
115
122
  if validation_result.no_problems:
116
123
  logger.debug("No validation errors found.")
117
124
  print(NO_VALIDATION_ERRORS_FOUND_MSG)
@@ -143,6 +150,7 @@ def _validate_data(
143
150
  parsed_resources: list[ParsedResource],
144
151
  config: ValidateDataConfig,
145
152
  shortcode: str,
153
+ existing_resources_retrieved: ExistingResourcesRetrieved,
146
154
  ) -> ValidateDataResult:
147
155
  logger.debug(f"Validate-data called with the following config: {vars(config)}")
148
156
  # Check if unknown classes are used
@@ -171,7 +179,7 @@ def _validate_data(
171
179
  )
172
180
  return ValidateDataResult(False, sorted_problems, report)
173
181
  reformatted = reformat_validation_graph(report)
174
- sorted_problems = sort_user_problems(reformatted, duplicate_file_warnings, shortcode)
182
+ sorted_problems = sort_user_problems(reformatted, duplicate_file_warnings, shortcode, existing_resources_retrieved)
175
183
  return ValidateDataResult(False, sorted_problems, report)
176
184
 
177
185
 
@@ -56,6 +56,7 @@ class UploadConfig:
56
56
  ignore_duplicate_files_warning: bool = False
57
57
  validation_severity: ValidationSeverity = field(default_factory=lambda: ValidationSeverity.INFO)
58
58
  id2iri_replacement_file: str | None = None
59
+ do_not_request_resource_metadata_from_db: bool = False
59
60
 
60
61
  def with_server_info(
61
62
  self,
@@ -177,6 +177,7 @@ def _handle_validation(
177
177
  ignore_duplicate_files_warning=ignore_duplicates,
178
178
  is_on_prod_server=is_on_prod_like_server,
179
179
  skip_ontology_validation=config.skip_ontology_validation,
180
+ do_not_request_resource_metadata_from_db=config.do_not_request_resource_metadata_from_db,
180
181
  ),
181
182
  auth=auth,
182
183
  )
@@ -154,3 +154,11 @@ class XmlUploadListNodeNotFoundError(BaseError):
154
154
 
155
155
  class UnknownDOAPException(BaseError):
156
156
  """Class for errors that are raised if a DOAP cannot be parsed"""
157
+
158
+
159
+ class CreateError(BaseError):
160
+ """Errors for the create command."""
161
+
162
+
163
+ class ProjectNotFoundError(CreateError):
164
+ """Class if a project is expected to exist but could not be found."""