dsp-tools 9.1.0.post11__py3-none-any.whl → 18.3.0.post13__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.
- dsp_tools/__init__.py +4 -0
- dsp_tools/cli/args.py +36 -0
- dsp_tools/cli/call_action.py +51 -231
- dsp_tools/cli/call_action_files_only.py +101 -0
- dsp_tools/cli/call_action_with_network.py +207 -0
- dsp_tools/cli/create_parsers.py +156 -58
- dsp_tools/cli/entry_point.py +56 -26
- dsp_tools/cli/utils.py +87 -0
- dsp_tools/clients/CLAUDE.md +420 -0
- dsp_tools/clients/authentication_client.py +14 -0
- dsp_tools/clients/authentication_client_live.py +66 -0
- dsp_tools/{utils → clients}/connection.py +2 -18
- dsp_tools/clients/connection_live.py +233 -0
- dsp_tools/clients/fuseki_metrics.py +60 -0
- dsp_tools/clients/group_user_clients.py +35 -0
- dsp_tools/clients/group_user_clients_live.py +181 -0
- dsp_tools/clients/legal_info_client.py +23 -0
- dsp_tools/clients/legal_info_client_live.py +132 -0
- dsp_tools/clients/list_client.py +49 -0
- dsp_tools/clients/list_client_live.py +166 -0
- dsp_tools/clients/metadata_client.py +24 -0
- dsp_tools/clients/metadata_client_live.py +47 -0
- dsp_tools/clients/ontology_clients.py +49 -0
- dsp_tools/clients/ontology_create_client_live.py +166 -0
- dsp_tools/clients/ontology_get_client_live.py +80 -0
- dsp_tools/clients/permissions_client.py +68 -0
- dsp_tools/clients/project_client.py +16 -0
- dsp_tools/clients/project_client_live.py +66 -0
- dsp_tools/commands/create/communicate_problems.py +24 -0
- dsp_tools/commands/create/create.py +134 -0
- dsp_tools/commands/create/create_on_server/cardinalities.py +111 -0
- dsp_tools/commands/create/create_on_server/classes.py +99 -0
- dsp_tools/commands/create/create_on_server/complete_ontologies.py +116 -0
- dsp_tools/commands/create/create_on_server/default_permissions.py +134 -0
- dsp_tools/commands/create/create_on_server/group_users.py +165 -0
- dsp_tools/commands/create/create_on_server/lists.py +163 -0
- dsp_tools/commands/create/create_on_server/mappers.py +12 -0
- dsp_tools/commands/create/create_on_server/onto_utils.py +74 -0
- dsp_tools/commands/create/create_on_server/ontology.py +52 -0
- dsp_tools/commands/create/create_on_server/project.py +68 -0
- dsp_tools/commands/create/create_on_server/properties.py +119 -0
- dsp_tools/commands/create/exceptions.py +29 -0
- dsp_tools/commands/create/lists_only.py +66 -0
- dsp_tools/commands/create/models/create_problems.py +87 -0
- dsp_tools/commands/create/models/parsed_ontology.py +88 -0
- dsp_tools/commands/create/models/parsed_project.py +81 -0
- dsp_tools/commands/create/models/rdf_ontology.py +12 -0
- dsp_tools/commands/create/models/server_project_info.py +100 -0
- dsp_tools/commands/create/parsing/parse_lists.py +45 -0
- dsp_tools/commands/create/parsing/parse_ontology.py +243 -0
- dsp_tools/commands/create/parsing/parse_project.py +149 -0
- dsp_tools/commands/create/parsing/parsing_utils.py +40 -0
- dsp_tools/commands/create/project_validate.py +595 -0
- dsp_tools/commands/create/serialisation/ontology.py +119 -0
- dsp_tools/commands/create/serialisation/project.py +44 -0
- dsp_tools/commands/excel2json/CLAUDE.md +101 -0
- dsp_tools/commands/excel2json/json_header.py +57 -23
- dsp_tools/commands/excel2json/{new_lists → lists}/compliance_checks.py +26 -26
- dsp_tools/commands/excel2json/{new_lists/make_new_lists.py → lists/make_lists.py} +19 -18
- dsp_tools/commands/excel2json/{new_lists → lists}/models/input_error.py +1 -12
- dsp_tools/commands/excel2json/{new_lists → lists}/models/serialise.py +9 -5
- dsp_tools/commands/excel2json/{new_lists → lists}/utils.py +4 -4
- dsp_tools/commands/excel2json/models/input_error.py +31 -11
- dsp_tools/commands/excel2json/models/json_header.py +53 -15
- dsp_tools/commands/excel2json/models/ontology.py +4 -3
- dsp_tools/commands/excel2json/{lists.py → old_lists.py} +26 -112
- dsp_tools/commands/excel2json/project.py +78 -34
- dsp_tools/commands/excel2json/properties.py +57 -36
- dsp_tools/commands/excel2json/resources.py +32 -12
- dsp_tools/commands/excel2json/utils.py +20 -1
- dsp_tools/commands/excel2xml/__init__.py +2 -2
- dsp_tools/commands/excel2xml/excel2xml_cli.py +7 -15
- dsp_tools/commands/excel2xml/excel2xml_lib.py +138 -493
- dsp_tools/commands/excel2xml/propertyelement.py +5 -5
- dsp_tools/commands/{project → get}/get.py +29 -13
- dsp_tools/commands/get/get_permissions.py +257 -0
- dsp_tools/commands/get/get_permissions_legacy.py +89 -0
- dsp_tools/commands/{project/models → get/legacy_models}/context.py +6 -6
- dsp_tools/commands/{project/models → get/legacy_models}/group.py +5 -10
- dsp_tools/commands/{project/models → get/legacy_models}/listnode.py +5 -35
- dsp_tools/commands/{project/models → get/legacy_models}/model.py +1 -1
- dsp_tools/commands/{project/models → get/legacy_models}/ontology.py +9 -14
- dsp_tools/commands/{project/models → get/legacy_models}/project.py +13 -6
- dsp_tools/commands/{project/models → get/legacy_models}/propertyclass.py +9 -16
- dsp_tools/commands/{project/models → get/legacy_models}/resourceclass.py +8 -46
- dsp_tools/commands/{project/models → get/legacy_models}/user.py +19 -60
- dsp_tools/commands/get/models/permissions_models.py +10 -0
- dsp_tools/commands/id2iri.py +20 -10
- dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +81 -56
- dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +4 -10
- dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +97 -37
- dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +2 -2
- dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +9 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +3 -3
- dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +2 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +12 -2
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +8 -9
- dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +18 -18
- dsp_tools/commands/start_stack.py +126 -77
- dsp_tools/commands/update_legal/CLAUDE.md +344 -0
- dsp_tools/commands/update_legal/__init__.py +0 -0
- dsp_tools/commands/update_legal/core.py +182 -0
- dsp_tools/commands/update_legal/csv_operations.py +135 -0
- dsp_tools/commands/update_legal/models.py +87 -0
- dsp_tools/commands/update_legal/xml_operations.py +247 -0
- dsp_tools/commands/validate_data/CLAUDE.md +159 -0
- dsp_tools/commands/validate_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/constants.py +59 -0
- dsp_tools/commands/validate_data/mappers.py +143 -0
- dsp_tools/commands/validate_data/models/__init__.py +0 -0
- dsp_tools/commands/validate_data/models/api_responses.py +45 -0
- dsp_tools/commands/validate_data/models/input_problems.py +119 -0
- dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
- dsp_tools/commands/validate_data/models/validation.py +106 -0
- dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
- dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
- dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
- dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
- dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
- dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
- dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
- dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
- dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
- dsp_tools/commands/{xml_validate/sparql/resource_shacl.py → validate_data/sparql/cardinality_shacl.py} +45 -47
- dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
- dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
- dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
- dsp_tools/commands/validate_data/utils.py +59 -0
- dsp_tools/commands/validate_data/validate_data.py +283 -0
- dsp_tools/commands/validate_data/validation/__init__.py +0 -0
- dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
- dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
- dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
- dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
- dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
- dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
- dsp_tools/commands/xmlupload/models/formatted_text_value.py +0 -25
- dsp_tools/commands/xmlupload/models/ingest.py +56 -70
- dsp_tools/commands/xmlupload/models/input_problems.py +6 -14
- dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
- dsp_tools/commands/xmlupload/models/permission.py +0 -39
- dsp_tools/commands/xmlupload/models/{deserialise/xmlpermission.py → permissions_parsed.py} +2 -2
- dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
- dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
- dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
- dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
- dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
- dsp_tools/commands/xmlupload/models/upload_clients.py +3 -3
- dsp_tools/commands/xmlupload/models/upload_state.py +2 -4
- dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
- dsp_tools/commands/xmlupload/{ark2iri.py → prepare_xml_input/ark2iri.py} +1 -1
- dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
- dsp_tools/commands/xmlupload/{iiif_uri_validator.py → prepare_xml_input/iiif_uri_validator.py} +2 -14
- dsp_tools/commands/xmlupload/{list_client.py → prepare_xml_input/list_client.py} +15 -10
- dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
- dsp_tools/commands/xmlupload/resource_create_client.py +7 -468
- dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
- dsp_tools/commands/xmlupload/stash/{construct_and_analyze_graph.py → analyse_circular_reference_graph.py} +64 -157
- dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
- dsp_tools/commands/xmlupload/stash/graph_models.py +13 -8
- dsp_tools/commands/xmlupload/stash/stash_circular_references.py +48 -115
- dsp_tools/commands/xmlupload/stash/stash_models.py +4 -9
- dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +34 -40
- dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +98 -108
- dsp_tools/commands/xmlupload/upload_config.py +8 -0
- dsp_tools/commands/xmlupload/write_diagnostic_info.py +14 -9
- dsp_tools/commands/xmlupload/xmlupload.py +214 -192
- dsp_tools/config/__init__.py +0 -0
- dsp_tools/config/logger_config.py +69 -0
- dsp_tools/{utils → config}/warnings_config.py +4 -1
- dsp_tools/error/__init__.py +0 -0
- dsp_tools/error/custom_warnings.py +39 -0
- dsp_tools/error/exceptions.py +204 -0
- dsp_tools/error/problems.py +10 -0
- dsp_tools/error/xmllib_errors.py +20 -0
- dsp_tools/error/xmllib_warnings.py +54 -0
- dsp_tools/error/xmllib_warnings_util.py +159 -0
- dsp_tools/error/xsd_validation_error_msg.py +19 -0
- dsp_tools/legacy_models/__init__.py +0 -0
- dsp_tools/{models → legacy_models}/datetimestamp.py +7 -7
- dsp_tools/{models → legacy_models}/langstring.py +1 -1
- dsp_tools/{models → legacy_models}/projectContext.py +4 -4
- dsp_tools/resources/schema/data.xsd +108 -83
- dsp_tools/resources/schema/lists-only.json +4 -23
- dsp_tools/resources/schema/project.json +80 -35
- dsp_tools/resources/schema/properties-only.json +1 -4
- dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
- dsp_tools/resources/start-stack/docker-compose.yml +34 -30
- dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
- dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
- dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
- dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
- dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
- dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
- dsp_tools/utils/ansi_colors.py +32 -0
- dsp_tools/utils/data_formats/__init__.py +0 -0
- dsp_tools/utils/{date_util.py → data_formats/date_util.py} +13 -1
- dsp_tools/utils/data_formats/iri_util.py +30 -0
- dsp_tools/utils/{shared.py → data_formats/shared.py} +1 -35
- dsp_tools/utils/{uri_util.py → data_formats/uri_util.py} +12 -2
- dsp_tools/utils/fuseki_bloating.py +63 -0
- dsp_tools/utils/json_parsing.py +22 -0
- dsp_tools/utils/rdf_constants.py +42 -0
- dsp_tools/utils/rdflib_utils.py +10 -0
- dsp_tools/utils/replace_id_with_iri.py +66 -0
- dsp_tools/utils/request_utils.py +238 -0
- dsp_tools/utils/xml_parsing/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
- dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
- dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
- dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
- dsp_tools/xmllib/CLAUDE.md +302 -0
- dsp_tools/xmllib/__init__.py +49 -0
- dsp_tools/xmllib/general_functions.py +877 -0
- dsp_tools/xmllib/internal/__init__.py +0 -0
- dsp_tools/xmllib/internal/checkers.py +162 -0
- dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
- dsp_tools/xmllib/internal/constants.py +46 -0
- dsp_tools/xmllib/internal/input_converters.py +155 -0
- dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
- dsp_tools/xmllib/internal/serialise_resource.py +177 -0
- dsp_tools/xmllib/internal/serialise_values.py +152 -0
- dsp_tools/xmllib/internal/type_aliases.py +11 -0
- dsp_tools/xmllib/models/config_options.py +28 -0
- dsp_tools/xmllib/models/date_formats.py +48 -0
- dsp_tools/xmllib/models/dsp_base_resources.py +1380 -400
- dsp_tools/xmllib/models/internal/__init__.py +0 -0
- dsp_tools/xmllib/models/internal/file_values.py +172 -0
- dsp_tools/xmllib/models/internal/geometry.py +162 -0
- dsp_tools/xmllib/models/{migration_metadata.py → internal/migration_metadata.py} +14 -10
- dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
- dsp_tools/xmllib/models/internal/values.py +342 -0
- dsp_tools/xmllib/models/licenses/__init__.py +0 -0
- dsp_tools/xmllib/models/licenses/other.py +59 -0
- dsp_tools/xmllib/models/licenses/recommended.py +107 -0
- dsp_tools/xmllib/models/permissions.py +41 -0
- dsp_tools/xmllib/models/res.py +1782 -0
- dsp_tools/xmllib/models/root.py +313 -26
- dsp_tools/xmllib/value_checkers.py +310 -47
- dsp_tools/xmllib/value_converters.py +765 -8
- dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
- dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
- dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
- {dsp_tools-9.1.0.post11.dist-info → dsp_tools-18.3.0.post13.dist-info}/entry_points.txt +1 -0
- dsp_tools/commands/project/create/project_create.py +0 -1107
- dsp_tools/commands/project/create/project_create_lists.py +0 -204
- dsp_tools/commands/project/create/project_validate.py +0 -453
- dsp_tools/commands/project/models/project_definition.py +0 -12
- dsp_tools/commands/rosetta.py +0 -124
- dsp_tools/commands/template.py +0 -30
- dsp_tools/commands/xml_validate/api_connection.py +0 -122
- dsp_tools/commands/xml_validate/deserialise_input.py +0 -135
- dsp_tools/commands/xml_validate/make_data_rdf.py +0 -193
- dsp_tools/commands/xml_validate/models/data_deserialised.py +0 -108
- dsp_tools/commands/xml_validate/models/data_rdf.py +0 -214
- dsp_tools/commands/xml_validate/models/input_problems.py +0 -191
- dsp_tools/commands/xml_validate/models/validation.py +0 -29
- dsp_tools/commands/xml_validate/reformat_validaton_result.py +0 -89
- dsp_tools/commands/xml_validate/sparql/construct_shapes.py +0 -16
- dsp_tools/commands/xml_validate/xml_validate.py +0 -151
- dsp_tools/commands/xmlupload/check_consistency_with_ontology.py +0 -253
- dsp_tools/commands/xmlupload/models/deserialise/deserialise_value.py +0 -236
- dsp_tools/commands/xmlupload/models/deserialise/xmlresource.py +0 -171
- dsp_tools/commands/xmlupload/models/namespace_context.py +0 -39
- dsp_tools/commands/xmlupload/models/ontology_lookup_models.py +0 -161
- dsp_tools/commands/xmlupload/models/ontology_problem_models.py +0 -178
- dsp_tools/commands/xmlupload/models/serialise/jsonld_serialiser.py +0 -40
- dsp_tools/commands/xmlupload/models/serialise/serialise_value.py +0 -51
- dsp_tools/commands/xmlupload/ontology_client.py +0 -92
- dsp_tools/commands/xmlupload/project_client.py +0 -91
- dsp_tools/commands/xmlupload/read_validate_xml_file.py +0 -99
- dsp_tools/models/custom_warnings.py +0 -31
- dsp_tools/models/exceptions.py +0 -90
- dsp_tools/resources/0100-template-repo/template.json +0 -45
- dsp_tools/resources/0100-template-repo/template.xml +0 -27
- dsp_tools/resources/start-stack/docker-compose-validation.yml +0 -5
- dsp_tools/resources/start-stack/start-stack-config.yml +0 -4
- dsp_tools/resources/xml_validate/api-shapes.ttl +0 -411
- dsp_tools/resources/xml_validate/replace_namespace.xslt +0 -61
- dsp_tools/utils/connection_live.py +0 -383
- dsp_tools/utils/iri_util.py +0 -14
- dsp_tools/utils/logger_config.py +0 -41
- dsp_tools/utils/set_encoder.py +0 -20
- dsp_tools/utils/xml_utils.py +0 -145
- dsp_tools/utils/xml_validation.py +0 -197
- dsp_tools/utils/xml_validation_models.py +0 -68
- dsp_tools/xmllib/models/file_values.py +0 -78
- dsp_tools/xmllib/models/resource.py +0 -415
- dsp_tools/xmllib/models/values.py +0 -428
- dsp_tools-9.1.0.post11.dist-info/METADATA +0 -130
- dsp_tools-9.1.0.post11.dist-info/RECORD +0 -167
- dsp_tools-9.1.0.post11.dist-info/WHEEL +0 -4
- dsp_tools-9.1.0.post11.dist-info/licenses/LICENSE +0 -674
- /dsp_tools/{commands/excel2json/new_lists → clients}/__init__.py +0 -0
- /dsp_tools/commands/{excel2json/new_lists/models → create}/__init__.py +0 -0
- /dsp_tools/commands/{project → create/create_on_server}/__init__.py +0 -0
- /dsp_tools/commands/{project/create → create/models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → create/parsing}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate → create/serialisation}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/models → excel2json/lists}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/sparql → excel2json/lists/models}/__init__.py +0 -0
- /dsp_tools/commands/excel2json/{new_lists → lists}/models/deserialise.py +0 -0
- /dsp_tools/commands/{xmlupload/models/deserialise → get}/__init__.py +0 -0
- /dsp_tools/commands/{xmlupload/models/serialise → get/legacy_models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → get/legacy_models}/helpers.py +0 -0
- /dsp_tools/{models → commands/get/models}/__init__.py +0 -0
|
@@ -8,46 +8,56 @@ from pathlib import Path
|
|
|
8
8
|
from typing import Never
|
|
9
9
|
|
|
10
10
|
from loguru import logger
|
|
11
|
-
from
|
|
11
|
+
from rdflib import URIRef
|
|
12
12
|
from tqdm import tqdm
|
|
13
13
|
|
|
14
14
|
from dsp_tools.cli.args import ServerCredentials
|
|
15
|
-
from dsp_tools.
|
|
16
|
-
from dsp_tools.
|
|
17
|
-
from dsp_tools.
|
|
18
|
-
from dsp_tools.
|
|
19
|
-
from dsp_tools.
|
|
20
|
-
from dsp_tools.
|
|
15
|
+
from dsp_tools.cli.args import ValidateDataConfig
|
|
16
|
+
from dsp_tools.cli.args import ValidationSeverity
|
|
17
|
+
from dsp_tools.clients.authentication_client import AuthenticationClient
|
|
18
|
+
from dsp_tools.clients.authentication_client_live import AuthenticationClientLive
|
|
19
|
+
from dsp_tools.clients.connection import Connection
|
|
20
|
+
from dsp_tools.clients.connection_live import ConnectionLive
|
|
21
|
+
from dsp_tools.clients.fuseki_metrics import FusekiMetrics
|
|
22
|
+
from dsp_tools.clients.legal_info_client import LegalInfoClient
|
|
23
|
+
from dsp_tools.clients.legal_info_client_live import LegalInfoClientLive
|
|
24
|
+
from dsp_tools.clients.project_client import ProjectClient
|
|
25
|
+
from dsp_tools.clients.project_client_live import ProjectClientLive
|
|
26
|
+
from dsp_tools.commands.validate_data.validate_data import validate_parsed_resources
|
|
27
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.make_resource_and_values import create_resource_with_values
|
|
21
28
|
from dsp_tools.commands.xmlupload.models.ingest import AssetClient
|
|
22
29
|
from dsp_tools.commands.xmlupload.models.ingest import DspIngestClientLive
|
|
23
|
-
from dsp_tools.commands.xmlupload.models.
|
|
24
|
-
from dsp_tools.commands.xmlupload.models.
|
|
30
|
+
from dsp_tools.commands.xmlupload.models.lookup_models import IRILookups
|
|
31
|
+
from dsp_tools.commands.xmlupload.models.lookup_models import XmlReferenceLookups
|
|
32
|
+
from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
|
|
25
33
|
from dsp_tools.commands.xmlupload.models.upload_clients import UploadClients
|
|
26
34
|
from dsp_tools.commands.xmlupload.models.upload_state import UploadState
|
|
27
|
-
from dsp_tools.commands.xmlupload.
|
|
28
|
-
from dsp_tools.commands.xmlupload.
|
|
29
|
-
from dsp_tools.commands.xmlupload.
|
|
30
|
-
from dsp_tools.commands.xmlupload.
|
|
31
|
-
from dsp_tools.commands.xmlupload.
|
|
32
|
-
from dsp_tools.commands.xmlupload.read_validate_xml_file import
|
|
35
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.get_processed_resources import get_processed_resources
|
|
36
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClient
|
|
37
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClientLive
|
|
38
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.prepare_xml_input import get_parsed_resources_and_mappers
|
|
39
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.prepare_xml_input import get_stash_and_upload_order
|
|
40
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.read_validate_xml_file import check_if_bitstreams_exist
|
|
41
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.read_validate_xml_file import validate_iiif_uris
|
|
33
42
|
from dsp_tools.commands.xmlupload.resource_create_client import ResourceCreateClient
|
|
34
|
-
from dsp_tools.commands.xmlupload.stash.stash_circular_references import identify_circular_references
|
|
35
|
-
from dsp_tools.commands.xmlupload.stash.stash_circular_references import stash_circular_references
|
|
36
|
-
from dsp_tools.commands.xmlupload.stash.stash_models import Stash
|
|
37
43
|
from dsp_tools.commands.xmlupload.stash.upload_stashed_resptr_props import upload_stashed_resptr_props
|
|
38
44
|
from dsp_tools.commands.xmlupload.stash.upload_stashed_xml_texts import upload_stashed_xml_texts
|
|
39
45
|
from dsp_tools.commands.xmlupload.upload_config import UploadConfig
|
|
40
46
|
from dsp_tools.commands.xmlupload.write_diagnostic_info import write_id2iri_mapping
|
|
41
|
-
from dsp_tools.
|
|
42
|
-
from dsp_tools.
|
|
43
|
-
from dsp_tools.
|
|
44
|
-
from dsp_tools.
|
|
45
|
-
from dsp_tools.
|
|
46
|
-
from dsp_tools.
|
|
47
|
-
from dsp_tools.
|
|
48
|
-
from dsp_tools.utils.
|
|
49
|
-
from dsp_tools.utils.
|
|
50
|
-
from dsp_tools.utils.
|
|
47
|
+
from dsp_tools.config.logger_config import WARNINGS_SAVEPATH
|
|
48
|
+
from dsp_tools.error.custom_warnings import DspToolsUserWarning
|
|
49
|
+
from dsp_tools.error.exceptions import BaseError
|
|
50
|
+
from dsp_tools.error.exceptions import PermanentConnectionError
|
|
51
|
+
from dsp_tools.error.exceptions import PermanentTimeOutError
|
|
52
|
+
from dsp_tools.error.exceptions import XmlUploadInterruptedError
|
|
53
|
+
from dsp_tools.utils.ansi_colors import BOLD_RED
|
|
54
|
+
from dsp_tools.utils.ansi_colors import BOLD_YELLOW
|
|
55
|
+
from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
|
|
56
|
+
from dsp_tools.utils.data_formats.uri_util import is_prod_like_server
|
|
57
|
+
from dsp_tools.utils.fuseki_bloating import communicate_fuseki_bloating
|
|
58
|
+
from dsp_tools.utils.replace_id_with_iri import use_id2iri_mapping_to_replace_ids
|
|
59
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedResource
|
|
60
|
+
from dsp_tools.utils.xml_parsing.parse_clean_validate_xml import parse_and_clean_xml_file
|
|
51
61
|
|
|
52
62
|
|
|
53
63
|
def xmlupload(
|
|
@@ -67,7 +77,7 @@ def xmlupload(
|
|
|
67
77
|
|
|
68
78
|
Raises:
|
|
69
79
|
BaseError: in case of permanent network or software failure
|
|
70
|
-
|
|
80
|
+
InputError: in case of permanent network or software failure, or if the XML file is invalid
|
|
71
81
|
InputError: in case of permanent network or software failure, or if the XML file is invalid
|
|
72
82
|
|
|
73
83
|
Returns:
|
|
@@ -75,66 +85,143 @@ def xmlupload(
|
|
|
75
85
|
uploaded because there is an error in it
|
|
76
86
|
"""
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if not config.skip_iiif_validation:
|
|
81
|
-
_validate_iiif_uris(root)
|
|
88
|
+
root = parse_and_clean_xml_file(input_file)
|
|
89
|
+
shortcode = root.attrib["shortcode"]
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
con
|
|
91
|
+
auth = AuthenticationClientLive(server=creds.server, email=creds.user, password=creds.password)
|
|
92
|
+
con = ConnectionLive(creds.server, auth)
|
|
85
93
|
config = config.with_server_info(server=creds.server, shortcode=shortcode)
|
|
94
|
+
clients = _get_live_clients(con, auth, creds, shortcode, imgdir)
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
parsed_resources, lookups = get_parsed_resources_and_mappers(root, clients)
|
|
97
|
+
if config.id2iri_file:
|
|
98
|
+
parsed_resources = use_id2iri_mapping_to_replace_ids(parsed_resources, Path(config.id2iri_file))
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
state = UploadState(resources, stash, config, permissions_lookup)
|
|
100
|
+
is_on_prod_like_server = is_prod_like_server(creds.server)
|
|
92
101
|
|
|
93
|
-
|
|
102
|
+
validation_ok = _handle_validation(
|
|
103
|
+
parsed_resources=parsed_resources,
|
|
104
|
+
lookups=lookups,
|
|
105
|
+
config=config,
|
|
106
|
+
is_on_prod_like_server=is_on_prod_like_server,
|
|
107
|
+
auth=auth,
|
|
108
|
+
input_file=input_file,
|
|
109
|
+
)
|
|
110
|
+
if not validation_ok:
|
|
111
|
+
return False
|
|
94
112
|
|
|
113
|
+
check_if_bitstreams_exist(root, imgdir)
|
|
114
|
+
if not config.skip_iiif_validation:
|
|
115
|
+
validate_iiif_uris(root)
|
|
95
116
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
This function takes a path to an XML file.
|
|
99
|
-
It validates the file against the XML schema.
|
|
100
|
-
It checks if all the mentioned bitstream files are in the specified location.
|
|
101
|
-
It retrieves the shortcode and default ontology from the XML file.
|
|
117
|
+
if not is_on_prod_like_server:
|
|
118
|
+
enable_unknown_license_if_any_are_missing(clients.legal_info_client, parsed_resources)
|
|
102
119
|
|
|
103
|
-
|
|
104
|
-
imgdir: directory to the bitstream files
|
|
105
|
-
input_file: file that will be pased
|
|
120
|
+
processed_resources = get_processed_resources(parsed_resources, lookups, is_on_prod_like_server)
|
|
106
121
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
sorted_resources, stash = get_stash_and_upload_order(processed_resources)
|
|
123
|
+
state = UploadState(
|
|
124
|
+
pending_resources=sorted_resources,
|
|
125
|
+
pending_stash=stash,
|
|
126
|
+
config=config,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return execute_upload(clients, state)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _handle_validation(
|
|
133
|
+
parsed_resources: list[ParsedResource],
|
|
134
|
+
lookups: XmlReferenceLookups,
|
|
135
|
+
config: UploadConfig,
|
|
136
|
+
is_on_prod_like_server: bool,
|
|
137
|
+
auth: AuthenticationClient,
|
|
138
|
+
input_file: Path,
|
|
139
|
+
) -> bool:
|
|
140
|
+
validation_should_be_skipped = config.skip_validation
|
|
141
|
+
if is_on_prod_like_server and config.skip_validation:
|
|
142
|
+
msg = (
|
|
143
|
+
"You set the flag '--skip-validation' to circumvent the SHACL schema validation. "
|
|
144
|
+
"This means that the upload may fail due to undetected errors. "
|
|
145
|
+
"Do you wish to skip the validation (yes/no)? "
|
|
146
|
+
)
|
|
147
|
+
resp = ""
|
|
148
|
+
while resp not in ["yes", "no"]:
|
|
149
|
+
resp = input(BOLD_RED + msg + RESET_TO_DEFAULT)
|
|
150
|
+
if str(resp) == "no":
|
|
151
|
+
validation_should_be_skipped = False
|
|
152
|
+
if not validation_should_be_skipped:
|
|
153
|
+
ignore_duplicates = config.ignore_duplicate_files_warning
|
|
154
|
+
if is_on_prod_like_server and ignore_duplicates:
|
|
155
|
+
msg = (
|
|
156
|
+
"You set the flag '--ignore-duplicate-files-warning'. "
|
|
157
|
+
"This means that duplicate multimedia files will not be detected. "
|
|
158
|
+
"Are you sure you want to exclude this from the validation? (yes/no)"
|
|
159
|
+
)
|
|
160
|
+
resp = ""
|
|
161
|
+
while resp not in ["yes", "no"]:
|
|
162
|
+
resp = input(BOLD_RED + msg + RESET_TO_DEFAULT)
|
|
163
|
+
if str(resp) == "no":
|
|
164
|
+
ignore_duplicates = False
|
|
165
|
+
v_severity = config.validation_severity
|
|
166
|
+
if is_on_prod_like_server:
|
|
167
|
+
v_severity = ValidationSeverity.INFO
|
|
168
|
+
validation_passed = validate_parsed_resources(
|
|
169
|
+
parsed_resources=parsed_resources,
|
|
170
|
+
authorship_lookup=lookups.authorships,
|
|
171
|
+
permission_ids=list(lookups.permissions.keys()),
|
|
172
|
+
shortcode=config.shortcode,
|
|
173
|
+
config=ValidateDataConfig(
|
|
174
|
+
input_file,
|
|
175
|
+
save_graph_dir=None,
|
|
176
|
+
severity=v_severity,
|
|
177
|
+
ignore_duplicate_files_warning=ignore_duplicates,
|
|
178
|
+
is_on_prod_server=is_on_prod_like_server,
|
|
179
|
+
skip_ontology_validation=config.skip_ontology_validation,
|
|
180
|
+
do_not_request_resource_metadata_from_db=config.do_not_request_resource_metadata_from_db,
|
|
181
|
+
),
|
|
182
|
+
auth=auth,
|
|
183
|
+
)
|
|
184
|
+
if not validation_passed:
|
|
185
|
+
return False
|
|
186
|
+
else:
|
|
187
|
+
logger.debug("SHACL validation was skipped.")
|
|
188
|
+
return True
|
|
114
189
|
|
|
115
190
|
|
|
116
191
|
def _get_live_clients(
|
|
117
192
|
con: Connection,
|
|
193
|
+
auth: AuthenticationClient,
|
|
118
194
|
creds: ServerCredentials,
|
|
119
195
|
shortcode: str,
|
|
120
196
|
imgdir: str,
|
|
121
197
|
) -> UploadClients:
|
|
122
198
|
ingest_client: AssetClient
|
|
123
|
-
ingest_client = DspIngestClientLive(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
imgdir=imgdir,
|
|
128
|
-
)
|
|
129
|
-
project_client: ProjectClient = ProjectClientLive(con, shortcode)
|
|
130
|
-
list_client: ListClient = ListClientLive(con, project_client.get_project_iri())
|
|
199
|
+
ingest_client = DspIngestClientLive(creds.dsp_ingest_url, auth, shortcode, imgdir)
|
|
200
|
+
project_client: ProjectClient = ProjectClientLive(auth.server, auth)
|
|
201
|
+
list_client: ListClient = ListClientLive(con, project_client.get_project_iri(shortcode))
|
|
202
|
+
legal_info_client: LegalInfoClient = LegalInfoClientLive(creds.server, shortcode, auth)
|
|
131
203
|
return UploadClients(
|
|
132
204
|
asset_client=ingest_client,
|
|
133
|
-
project_client=project_client,
|
|
134
205
|
list_client=list_client,
|
|
206
|
+
legal_info_client=legal_info_client,
|
|
135
207
|
)
|
|
136
208
|
|
|
137
209
|
|
|
210
|
+
def enable_unknown_license_if_any_are_missing(
|
|
211
|
+
legal_info_client: LegalInfoClient, parsed_resources: list[ParsedResource]
|
|
212
|
+
) -> None:
|
|
213
|
+
all_license_infos = [x.file_value.metadata.license_iri for x in parsed_resources if x.file_value]
|
|
214
|
+
if not all(all_license_infos):
|
|
215
|
+
legal_info_client.enable_unknown_license()
|
|
216
|
+
msg = (
|
|
217
|
+
"The files or iiif-uris in your data are missing some legal information. "
|
|
218
|
+
"To facilitate an upload on a test environment we are adding dummy information.\n"
|
|
219
|
+
"In order to be able to use the license 'unknown' in place of missing licenses, "
|
|
220
|
+
"we are enabling it for your project."
|
|
221
|
+
)
|
|
222
|
+
print(BOLD_YELLOW, msg, RESET_TO_DEFAULT)
|
|
223
|
+
|
|
224
|
+
|
|
138
225
|
def execute_upload(clients: UploadClients, upload_state: UploadState) -> bool:
|
|
139
226
|
"""Execute an upload from an upload state, and clean up afterwards.
|
|
140
227
|
|
|
@@ -145,29 +232,33 @@ def execute_upload(clients: UploadClients, upload_state: UploadState) -> bool:
|
|
|
145
232
|
Returns:
|
|
146
233
|
True if all resources could be uploaded without errors; False if any resource could not be uploaded
|
|
147
234
|
"""
|
|
235
|
+
logger.debug("Start uploading data")
|
|
236
|
+
db_metrics = None
|
|
237
|
+
if clients.legal_info_client.server == "http://0.0.0.0:3333":
|
|
238
|
+
db_metrics = FusekiMetrics()
|
|
239
|
+
db_metrics.try_get_start_size()
|
|
240
|
+
_upload_copyright_holders(upload_state.pending_resources, clients.legal_info_client)
|
|
148
241
|
_upload_resources(clients, upload_state)
|
|
242
|
+
if db_metrics is not None:
|
|
243
|
+
db_metrics.try_get_end_size()
|
|
244
|
+
communicate_fuseki_bloating(db_metrics)
|
|
149
245
|
return _cleanup_upload(upload_state)
|
|
150
246
|
|
|
151
247
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
"""Do the consistency check, resolve circular references, and return the resources and permissions."""
|
|
157
|
-
do_xml_consistency_check_with_ontology(onto_client=ontology_client, root=root)
|
|
158
|
-
return _resolve_circular_references(
|
|
159
|
-
root=root,
|
|
160
|
-
con=ontology_client.con,
|
|
161
|
-
default_ontology=ontology_client.default_ontology,
|
|
162
|
-
)
|
|
248
|
+
def _upload_copyright_holders(resources: list[ProcessedResource], legal_info_client: LegalInfoClient) -> None:
|
|
249
|
+
logger.debug("Get and upload copyright holders")
|
|
250
|
+
copyright_holders = _get_copyright_holders(resources)
|
|
251
|
+
legal_info_client.post_copyright_holders(copyright_holders)
|
|
163
252
|
|
|
164
253
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
254
|
+
def _get_copyright_holders(resources: list[ProcessedResource]) -> list[str]:
|
|
255
|
+
copyright_holders = set()
|
|
256
|
+
for res in resources:
|
|
257
|
+
if res.file_value:
|
|
258
|
+
copyright_holders.add(res.file_value.metadata.copyright_holder)
|
|
259
|
+
elif res.iiif_uri:
|
|
260
|
+
copyright_holders.add(res.iiif_uri.metadata.copyright_holder)
|
|
261
|
+
return [x for x in copyright_holders if x]
|
|
171
262
|
|
|
172
263
|
|
|
173
264
|
def _cleanup_upload(upload_state: UploadState) -> bool:
|
|
@@ -180,7 +271,11 @@ def _cleanup_upload(upload_state: UploadState) -> bool:
|
|
|
180
271
|
Returns:
|
|
181
272
|
success status (deduced from failed_uploads and non-applied stash)
|
|
182
273
|
"""
|
|
183
|
-
write_id2iri_mapping(
|
|
274
|
+
write_id2iri_mapping(
|
|
275
|
+
id2iri_mapping=upload_state.iri_resolver.lookup,
|
|
276
|
+
shortcode=upload_state.config.shortcode,
|
|
277
|
+
diagnostics=upload_state.config.diagnostics,
|
|
278
|
+
)
|
|
184
279
|
has_stash_failed = upload_state.pending_stash and not upload_state.pending_stash.is_empty()
|
|
185
280
|
if not upload_state.failed_uploads and not has_stash_failed:
|
|
186
281
|
success = True
|
|
@@ -205,28 +300,6 @@ def _cleanup_upload(upload_state: UploadState) -> bool:
|
|
|
205
300
|
return success
|
|
206
301
|
|
|
207
302
|
|
|
208
|
-
def _resolve_circular_references(
|
|
209
|
-
root: etree._Element,
|
|
210
|
-
con: Connection,
|
|
211
|
-
default_ontology: str,
|
|
212
|
-
) -> tuple[list[XMLResource], dict[str, Permissions], Stash | None]:
|
|
213
|
-
logger.info("Checking resources for circular references...")
|
|
214
|
-
print(f"{datetime.now()}: Checking resources for circular references...")
|
|
215
|
-
stash_lookup, upload_order = identify_circular_references(root)
|
|
216
|
-
logger.info("Get data from XML...")
|
|
217
|
-
resources, permissions_lookup = _get_data_from_xml(
|
|
218
|
-
con=con,
|
|
219
|
-
root=root,
|
|
220
|
-
default_ontology=default_ontology,
|
|
221
|
-
)
|
|
222
|
-
sorting_lookup = {res.res_id: res for res in resources}
|
|
223
|
-
resources = [sorting_lookup[res_id] for res_id in upload_order]
|
|
224
|
-
logger.info("Stashing circular references...")
|
|
225
|
-
print(f"{datetime.now()}: Stashing circular references...")
|
|
226
|
-
stash = stash_circular_references(resources, stash_lookup, permissions_lookup)
|
|
227
|
-
return resources, permissions_lookup, stash
|
|
228
|
-
|
|
229
|
-
|
|
230
303
|
def _upload_resources(clients: UploadClients, upload_state: UploadState) -> None:
|
|
231
304
|
"""
|
|
232
305
|
Iterates through all resources and tries to upload them to DSP.
|
|
@@ -241,20 +314,16 @@ def _upload_resources(clients: UploadClients, upload_state: UploadState) -> None
|
|
|
241
314
|
BaseException: in case of an unhandled exception during resource creation
|
|
242
315
|
XmlUploadInterruptedError: if the number of resources created is equal to the interrupt_after value
|
|
243
316
|
"""
|
|
244
|
-
project_iri = clients.
|
|
245
|
-
project_onto_dict = clients.project_client.get_ontology_name_dict()
|
|
246
|
-
listnode_lookup = clients.list_client.get_list_node_id_to_iri_lookup()
|
|
317
|
+
project_iri = clients.list_client.project_iri
|
|
247
318
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
iri_resolver=upload_state.iri_resolver,
|
|
252
|
-
project_onto_dict=project_onto_dict,
|
|
253
|
-
permissions_lookup=upload_state.permissions_lookup,
|
|
254
|
-
listnode_lookup=listnode_lookup,
|
|
255
|
-
media_previously_ingested=upload_state.config.media_previously_uploaded,
|
|
319
|
+
iri_lookup = IRILookups(
|
|
320
|
+
project_iri=URIRef(project_iri),
|
|
321
|
+
id_to_iri=upload_state.iri_resolver,
|
|
256
322
|
)
|
|
257
323
|
|
|
324
|
+
resource_create_client = ResourceCreateClient(
|
|
325
|
+
con=clients.list_client.con,
|
|
326
|
+
)
|
|
258
327
|
progress_bar = tqdm(upload_state.pending_resources.copy(), desc="Creating Resources", dynamic_ncols=True)
|
|
259
328
|
try:
|
|
260
329
|
for creation_attempts_of_this_round, resource in enumerate(progress_bar):
|
|
@@ -263,104 +332,61 @@ def _upload_resources(clients: UploadClients, upload_state: UploadState) -> None
|
|
|
263
332
|
resource=resource,
|
|
264
333
|
ingest_client=clients.asset_client,
|
|
265
334
|
resource_create_client=resource_create_client,
|
|
335
|
+
iri_lookups=iri_lookup,
|
|
266
336
|
creation_attempts_of_this_round=creation_attempts_of_this_round,
|
|
267
337
|
)
|
|
268
338
|
progress_bar.set_description(f"Creating Resources (failed: {len(upload_state.failed_uploads)})")
|
|
269
339
|
if upload_state.pending_stash:
|
|
270
|
-
_upload_stash(upload_state, clients.
|
|
340
|
+
_upload_stash(upload_state, clients.list_client.con)
|
|
271
341
|
except XmlUploadInterruptedError as err:
|
|
272
342
|
_handle_upload_error(err, upload_state)
|
|
273
343
|
|
|
274
344
|
|
|
275
|
-
def _get_data_from_xml(
|
|
276
|
-
con: Connection,
|
|
277
|
-
root: etree._Element,
|
|
278
|
-
default_ontology: str,
|
|
279
|
-
) -> tuple[list[XMLResource], dict[str, Permissions]]:
|
|
280
|
-
proj_context = _get_project_context_from_server(connection=con, shortcode=root.attrib["shortcode"])
|
|
281
|
-
permissions = _extract_permissions_from_xml(root, proj_context)
|
|
282
|
-
resources = _extract_resources_from_xml(root, default_ontology)
|
|
283
|
-
permissions_lookup = {name: perm.get_permission_instance() for name, perm in permissions.items()}
|
|
284
|
-
return resources, permissions_lookup
|
|
285
|
-
|
|
286
|
-
|
|
287
345
|
def _upload_stash(
|
|
288
346
|
upload_state: UploadState,
|
|
289
|
-
|
|
347
|
+
con: Connection,
|
|
290
348
|
) -> None:
|
|
291
349
|
if upload_state.pending_stash and upload_state.pending_stash.standoff_stash:
|
|
292
|
-
upload_stashed_xml_texts(upload_state,
|
|
293
|
-
context = get_json_ld_context_for_project(project_client.get_ontology_name_dict())
|
|
350
|
+
upload_stashed_xml_texts(upload_state, con)
|
|
294
351
|
if upload_state.pending_stash and upload_state.pending_stash.link_value_stash:
|
|
295
|
-
upload_stashed_resptr_props(upload_state,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
def _get_project_context_from_server(connection: Connection, shortcode: str) -> ProjectContext:
|
|
299
|
-
"""
|
|
300
|
-
This function retrieves the project context previously uploaded on the server (json file)
|
|
301
|
-
|
|
302
|
-
Args:
|
|
303
|
-
connection: connection to the server
|
|
304
|
-
shortcode: shortcode of the project
|
|
305
|
-
|
|
306
|
-
Returns:
|
|
307
|
-
Project context
|
|
308
|
-
|
|
309
|
-
Raises:
|
|
310
|
-
UserError: If the project was not previously uploaded on the server
|
|
311
|
-
"""
|
|
312
|
-
try:
|
|
313
|
-
proj_context = ProjectContext(con=connection, shortcode=shortcode)
|
|
314
|
-
except BaseError:
|
|
315
|
-
logger.opt(exception=True).error("Unable to retrieve project context from DSP server")
|
|
316
|
-
raise UserError("Unable to retrieve project context from DSP server") from None
|
|
317
|
-
return proj_context
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
def _extract_permissions_from_xml(root: etree._Element, proj_context: ProjectContext) -> dict[str, XmlPermission]:
|
|
321
|
-
permission_ele = list(root.iter(tag="permissions"))
|
|
322
|
-
permissions = [XmlPermission(permission, proj_context) for permission in permission_ele]
|
|
323
|
-
return {permission.permission_id: permission for permission in permissions}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def _extract_resources_from_xml(root: etree._Element, default_ontology: str) -> list[XMLResource]:
|
|
327
|
-
resources = list(root.iter(tag="resource"))
|
|
328
|
-
return [XMLResource.from_node(res, default_ontology) for res in resources]
|
|
352
|
+
upload_stashed_resptr_props(upload_state, con)
|
|
329
353
|
|
|
330
354
|
|
|
331
355
|
def _upload_one_resource(
|
|
332
356
|
upload_state: UploadState,
|
|
333
|
-
resource:
|
|
357
|
+
resource: ProcessedResource,
|
|
334
358
|
ingest_client: AssetClient,
|
|
335
359
|
resource_create_client: ResourceCreateClient,
|
|
360
|
+
iri_lookups: IRILookups,
|
|
336
361
|
creation_attempts_of_this_round: int,
|
|
337
362
|
) -> None:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if not
|
|
363
|
+
media_info = None
|
|
364
|
+
if resource.file_value:
|
|
365
|
+
try:
|
|
366
|
+
ingest_result = ingest_client.get_bitstream_info(resource.file_value)
|
|
367
|
+
except PermanentConnectionError as err:
|
|
368
|
+
_handle_permanent_connection_error(err)
|
|
369
|
+
except KeyboardInterrupt:
|
|
370
|
+
_handle_keyboard_interrupt()
|
|
371
|
+
if not ingest_result:
|
|
347
372
|
upload_state.failed_uploads.append(resource.res_id)
|
|
348
373
|
return
|
|
349
|
-
|
|
350
|
-
_handle_permanent_connection_error(err)
|
|
351
|
-
except KeyboardInterrupt:
|
|
352
|
-
_handle_keyboard_interrupt()
|
|
374
|
+
media_info = ingest_result
|
|
353
375
|
|
|
354
376
|
iri = None
|
|
355
377
|
try:
|
|
356
|
-
|
|
378
|
+
serialised_resource = create_resource_with_values(
|
|
379
|
+
resource=resource, bitstream_information=media_info, lookups=iri_lookups
|
|
380
|
+
)
|
|
381
|
+
logger.info(f"Attempting to create resource {resource.res_id} (label: {resource.label})...")
|
|
382
|
+
iri = resource_create_client.create_resource(serialised_resource, resource_has_bitstream=bool(media_info))
|
|
357
383
|
except (PermanentTimeOutError, KeyboardInterrupt) as err:
|
|
358
384
|
_handle_permanent_timeout_or_keyboard_interrupt(err, resource.res_id)
|
|
359
385
|
except PermanentConnectionError as err:
|
|
360
386
|
_handle_permanent_connection_error(err)
|
|
361
387
|
except Exception as err: # noqa: BLE001 (blind-except)
|
|
362
388
|
err_msg = err.message if isinstance(err, BaseError) else None
|
|
363
|
-
|
|
389
|
+
_inform_about_resource_creation_failure(resource, err_msg)
|
|
364
390
|
|
|
365
391
|
try:
|
|
366
392
|
_tidy_up_resource_creation_idempotent(upload_state, iri, resource)
|
|
@@ -374,6 +400,7 @@ def _handle_permanent_connection_error(err: PermanentConnectionError) -> Never:
|
|
|
374
400
|
msg = "Lost connection to DSP server, probably because the server is down. "
|
|
375
401
|
msg += f"Please continue later with 'resume-xmlupload'. Reason for this failure: {err.message}"
|
|
376
402
|
logger.error(msg)
|
|
403
|
+
msg += f"\nSee {WARNINGS_SAVEPATH} for more information."
|
|
377
404
|
raise XmlUploadInterruptedError(msg) from None
|
|
378
405
|
|
|
379
406
|
|
|
@@ -410,7 +437,7 @@ def _interrupt_if_indicated(upload_state: UploadState, creation_attempts_of_this
|
|
|
410
437
|
def _tidy_up_resource_creation_idempotent(
|
|
411
438
|
upload_state: UploadState,
|
|
412
439
|
iri: str | None,
|
|
413
|
-
resource:
|
|
440
|
+
resource: ProcessedResource,
|
|
414
441
|
) -> None:
|
|
415
442
|
previous_successful = len(upload_state.iri_resolver.lookup)
|
|
416
443
|
previous_failed = len(upload_state.failed_uploads)
|
|
@@ -431,15 +458,10 @@ def _tidy_up_resource_creation_idempotent(
|
|
|
431
458
|
upload_state.pending_resources.remove(resource)
|
|
432
459
|
|
|
433
460
|
|
|
434
|
-
def
|
|
435
|
-
|
|
461
|
+
def _inform_about_resource_creation_failure(resource: ProcessedResource, err_msg: str | None) -> None:
|
|
462
|
+
log_msg = f"Unable to create resource '{resource.label}' ({resource.res_id})\n"
|
|
436
463
|
if err_msg:
|
|
437
|
-
|
|
438
|
-
log_msg = (
|
|
439
|
-
f"Unable to create resource '{resource.label}' ({resource.res_id})\n"
|
|
440
|
-
f"Resource details:\n{vars(resource)}\n"
|
|
441
|
-
f"Property details:\n" + "\n".join([str(vars(prop)) for prop in resource.properties])
|
|
442
|
-
)
|
|
464
|
+
log_msg += err_msg
|
|
443
465
|
logger.exception(log_msg)
|
|
444
466
|
|
|
445
467
|
|
|
@@ -476,7 +498,7 @@ def _handle_upload_error(err: BaseException, upload_state: UploadState) -> None:
|
|
|
476
498
|
msg += f"Independently from this, there were some resources that could not be uploaded: {failed}\n"
|
|
477
499
|
|
|
478
500
|
if exit_code == 1:
|
|
479
|
-
logger.
|
|
501
|
+
logger.error(msg)
|
|
480
502
|
else:
|
|
481
503
|
logger.info(msg)
|
|
482
504
|
print(msg)
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from dotenv import find_dotenv
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
load_dotenv(dotenv_path=find_dotenv(usecwd=True))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _make_and_get_logs_directory() -> Path:
|
|
13
|
+
"""Get the base .dsp-tools directory, creating it if it doesn't exist."""
|
|
14
|
+
base_dir = Path.home() / ".dsp-tools" / "logs"
|
|
15
|
+
base_dir.mkdir(exist_ok=True, parents=True)
|
|
16
|
+
return base_dir
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f")[:-3]
|
|
20
|
+
|
|
21
|
+
LOGGER_SAVEPATH = (_make_and_get_logs_directory() / f"{timestamp}_logging.log").absolute()
|
|
22
|
+
WARNINGS_SAVEPATH = Path("warnings.log")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def logger_config() -> None:
|
|
26
|
+
"""
|
|
27
|
+
This function configures the log files.
|
|
28
|
+
Currently, there are three sinks:
|
|
29
|
+
- timestamp_logging.log in ~/.dsp-tools/logs/ contains the entire stack-trace
|
|
30
|
+
- warnings.log in the cwd only with level warning and higher for the user (no stack-trace)
|
|
31
|
+
OR a complete logging.log file with the stack-trace if configured in the .env
|
|
32
|
+
- print output on the terminal, formatted the same as the warnings.log
|
|
33
|
+
"""
|
|
34
|
+
# If this is not removed, the default formatting is also printed out on the terminal
|
|
35
|
+
logger.remove()
|
|
36
|
+
|
|
37
|
+
text_format = "<level>{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}</level>"
|
|
38
|
+
rotation_size = "100 MB"
|
|
39
|
+
|
|
40
|
+
logger.add(
|
|
41
|
+
sink=LOGGER_SAVEPATH,
|
|
42
|
+
format=text_format,
|
|
43
|
+
backtrace=True,
|
|
44
|
+
diagnose=True,
|
|
45
|
+
delay=True,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
additional_log = str(os.getenv("DSP_TOOLS_SAVE_ADDITIONAL_LOG_FILE_IN_CWD"))
|
|
49
|
+
if additional_log.lower() == "true":
|
|
50
|
+
logger.add(
|
|
51
|
+
sink=Path("logging.log"),
|
|
52
|
+
format=text_format,
|
|
53
|
+
backtrace=True,
|
|
54
|
+
diagnose=True,
|
|
55
|
+
rotation=rotation_size,
|
|
56
|
+
retention=2,
|
|
57
|
+
delay=True,
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
logger.add(
|
|
61
|
+
sink=WARNINGS_SAVEPATH,
|
|
62
|
+
level="WARNING",
|
|
63
|
+
format=text_format,
|
|
64
|
+
backtrace=False,
|
|
65
|
+
diagnose=False,
|
|
66
|
+
rotation=rotation_size,
|
|
67
|
+
retention=2,
|
|
68
|
+
delay=True,
|
|
69
|
+
)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
from typing import TextIO
|
|
3
3
|
|
|
4
|
-
from dsp_tools.
|
|
4
|
+
from dsp_tools.error.custom_warnings import DspToolsWarning
|
|
5
|
+
from dsp_tools.error.xmllib_warnings import XmllibUserInfoBase
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def initialize_warnings() -> None:
|
|
@@ -23,6 +24,8 @@ def initialize_warnings() -> None:
|
|
|
23
24
|
) -> None:
|
|
24
25
|
if issubclass(category, DspToolsWarning):
|
|
25
26
|
category.showwarning(str(message))
|
|
27
|
+
elif issubclass(category, XmllibUserInfoBase):
|
|
28
|
+
category.showwarning(str(message))
|
|
26
29
|
else:
|
|
27
30
|
built_in_showwarning(message, category, filename, lineno, file, line)
|
|
28
31
|
|
|
File without changes
|