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
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from rdflib import RDF
|
|
4
|
+
from rdflib import XSD
|
|
5
|
+
from rdflib import BNode
|
|
6
|
+
from rdflib import Graph
|
|
7
|
+
from rdflib import Literal
|
|
8
|
+
from rdflib import URIRef
|
|
9
|
+
|
|
10
|
+
from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
|
|
11
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.constants import LINK_PROP_TYPE_INFO
|
|
12
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.constants import LIST_PROP_TYPE_INFO
|
|
13
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.constants import RDF_LITERAL_PROP_TYPE_MAPPER
|
|
14
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.constants import RICHTEXT_PROP_TYPE_INFO
|
|
15
|
+
from dsp_tools.commands.xmlupload.models.lookup_models import IRILookups
|
|
16
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
17
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedBoolean
|
|
18
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedColor
|
|
19
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedDate
|
|
20
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedDecimal
|
|
21
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedGeometry
|
|
22
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedGeoname
|
|
23
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedInt
|
|
24
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedInterval
|
|
25
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedLink
|
|
26
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedList
|
|
27
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedRichtext
|
|
28
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedSimpleText
|
|
29
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedTime
|
|
30
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedUri
|
|
31
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedValue
|
|
32
|
+
from dsp_tools.commands.xmlupload.models.rdf_models import RDFPropTypeInfo
|
|
33
|
+
from dsp_tools.commands.xmlupload.richtext_id2iri import prepare_richtext_string_for_upload
|
|
34
|
+
from dsp_tools.config.logger_config import WARNINGS_SAVEPATH
|
|
35
|
+
from dsp_tools.error.exceptions import BaseError
|
|
36
|
+
from dsp_tools.error.exceptions import InputError
|
|
37
|
+
from dsp_tools.utils.data_formats.date_util import DayMonthYearEra
|
|
38
|
+
from dsp_tools.utils.data_formats.date_util import SingleDate
|
|
39
|
+
from dsp_tools.utils.data_formats.date_util import StartEnd
|
|
40
|
+
from dsp_tools.utils.data_formats.iri_util import is_resource_iri
|
|
41
|
+
from dsp_tools.utils.rdf_constants import KNORA_API
|
|
42
|
+
|
|
43
|
+
type LiteralValueTypesAlias = Union[
|
|
44
|
+
ProcessedBoolean,
|
|
45
|
+
ProcessedColor,
|
|
46
|
+
ProcessedDecimal,
|
|
47
|
+
ProcessedGeoname,
|
|
48
|
+
ProcessedGeometry,
|
|
49
|
+
ProcessedInt,
|
|
50
|
+
ProcessedSimpleText,
|
|
51
|
+
ProcessedTime,
|
|
52
|
+
ProcessedUri,
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def make_values(values: list[ProcessedValue], res_node: BNode | URIRef, lookups: IRILookups) -> Graph:
|
|
57
|
+
"""
|
|
58
|
+
Serialise the values of a resource.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
values: list of ProcessedValues of the resource
|
|
62
|
+
res_node: node of the resource
|
|
63
|
+
lookups: lookups to resolve IRIs
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Graph with the values
|
|
67
|
+
"""
|
|
68
|
+
properties_graph = Graph()
|
|
69
|
+
for val in values:
|
|
70
|
+
single_prop_graph = _make_one_value_graph(val=val, res_node=res_node, iri_lookups=lookups)
|
|
71
|
+
properties_graph += single_prop_graph
|
|
72
|
+
return properties_graph
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _make_one_value_graph(val: ProcessedValue, res_node: BNode | URIRef, iri_lookups: IRILookups) -> Graph:
|
|
76
|
+
match val:
|
|
77
|
+
case (
|
|
78
|
+
ProcessedBoolean()
|
|
79
|
+
| ProcessedColor()
|
|
80
|
+
| ProcessedDecimal()
|
|
81
|
+
| ProcessedGeometry()
|
|
82
|
+
| ProcessedGeoname()
|
|
83
|
+
| ProcessedInt()
|
|
84
|
+
| ProcessedTime()
|
|
85
|
+
| ProcessedUri()
|
|
86
|
+
| ProcessedSimpleText()
|
|
87
|
+
):
|
|
88
|
+
literal_info = RDF_LITERAL_PROP_TYPE_MAPPER[type(val)]
|
|
89
|
+
properties_graph = _make_value_graph_with_literal_object(
|
|
90
|
+
val=val,
|
|
91
|
+
res_node=res_node,
|
|
92
|
+
prop_type_info=literal_info,
|
|
93
|
+
)
|
|
94
|
+
case ProcessedList():
|
|
95
|
+
properties_graph = _make_list_value_graph(val=val, res_node=res_node, prop_type_info=LIST_PROP_TYPE_INFO)
|
|
96
|
+
case ProcessedLink():
|
|
97
|
+
target_iri = _resolve_id_to_iri(val.value, iri_lookups.id_to_iri)
|
|
98
|
+
properties_graph = make_link_value_graph(
|
|
99
|
+
val=val,
|
|
100
|
+
val_node=BNode(),
|
|
101
|
+
res_node=res_node,
|
|
102
|
+
target_iri=URIRef(target_iri),
|
|
103
|
+
)
|
|
104
|
+
case ProcessedRichtext():
|
|
105
|
+
properties_graph = make_richtext_value_graph(
|
|
106
|
+
val=val,
|
|
107
|
+
val_node=BNode(),
|
|
108
|
+
res_node=res_node,
|
|
109
|
+
iri_resolver=iri_lookups.id_to_iri,
|
|
110
|
+
)
|
|
111
|
+
case ProcessedDate():
|
|
112
|
+
properties_graph = _make_date_value_graph(
|
|
113
|
+
val=val,
|
|
114
|
+
res_node=res_node,
|
|
115
|
+
)
|
|
116
|
+
case ProcessedInterval():
|
|
117
|
+
properties_graph = _make_interval_value_graph(
|
|
118
|
+
val=val,
|
|
119
|
+
res_node=res_node,
|
|
120
|
+
)
|
|
121
|
+
case _:
|
|
122
|
+
raise InputError(f"Unknown value type: {type(val).__name__}")
|
|
123
|
+
return properties_graph
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _make_base_value_graph(
|
|
127
|
+
val: ProcessedValue,
|
|
128
|
+
val_node: BNode | URIRef,
|
|
129
|
+
prop_type_info: RDFPropTypeInfo,
|
|
130
|
+
res_node: BNode | URIRef,
|
|
131
|
+
) -> Graph:
|
|
132
|
+
g = _add_optional_triples(val_node, val.permissions, val.comment)
|
|
133
|
+
g.add((res_node, URIRef(val.prop_iri), val_node))
|
|
134
|
+
g.add((val_node, RDF.type, prop_type_info.knora_type))
|
|
135
|
+
return g
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _add_optional_triples(val_bn: BNode | URIRef, permissions: Permissions | None, comment: str | None) -> Graph:
|
|
139
|
+
g = Graph()
|
|
140
|
+
if permissions:
|
|
141
|
+
g.add((val_bn, KNORA_API.hasPermissions, Literal(str(permissions), datatype=XSD.string)))
|
|
142
|
+
if comment:
|
|
143
|
+
g.add((val_bn, KNORA_API.valueHasComment, Literal(comment, datatype=XSD.string)))
|
|
144
|
+
return g
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _make_value_graph_with_literal_object(
|
|
148
|
+
val: LiteralValueTypesAlias,
|
|
149
|
+
prop_type_info: RDFPropTypeInfo,
|
|
150
|
+
res_node: BNode | URIRef,
|
|
151
|
+
) -> Graph:
|
|
152
|
+
val_bn = BNode()
|
|
153
|
+
g = _make_base_value_graph(val=val, val_node=val_bn, prop_type_info=prop_type_info, res_node=res_node)
|
|
154
|
+
g.add((val_bn, prop_type_info.knora_prop, Literal(val.value, datatype=prop_type_info.xsd_type)))
|
|
155
|
+
return g
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _make_list_value_graph(
|
|
159
|
+
val: ProcessedList,
|
|
160
|
+
res_node: BNode | URIRef,
|
|
161
|
+
prop_type_info: RDFPropTypeInfo,
|
|
162
|
+
) -> Graph:
|
|
163
|
+
val_bn = BNode()
|
|
164
|
+
g = _make_base_value_graph(val=val, val_node=val_bn, prop_type_info=prop_type_info, res_node=res_node)
|
|
165
|
+
g.add((val_bn, prop_type_info.knora_prop, URIRef(val.value)))
|
|
166
|
+
return g
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def make_link_value_graph(
|
|
170
|
+
val: ProcessedLink,
|
|
171
|
+
val_node: BNode | URIRef,
|
|
172
|
+
res_node: BNode | URIRef,
|
|
173
|
+
target_iri: URIRef,
|
|
174
|
+
) -> Graph:
|
|
175
|
+
"""Make a LinkValue Graph"""
|
|
176
|
+
g = _make_base_value_graph(val=val, val_node=val_node, prop_type_info=LINK_PROP_TYPE_INFO, res_node=res_node)
|
|
177
|
+
g.add((val_node, LINK_PROP_TYPE_INFO.knora_prop, target_iri))
|
|
178
|
+
return g
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _resolve_id_to_iri(value: str, iri_resolver: IriResolver) -> URIRef:
|
|
182
|
+
if is_resource_iri(value):
|
|
183
|
+
return URIRef(value)
|
|
184
|
+
elif resolved_iri := iri_resolver.get(value):
|
|
185
|
+
return URIRef(resolved_iri)
|
|
186
|
+
msg = (
|
|
187
|
+
f"Could not find the ID {value} in the id2iri mapping. "
|
|
188
|
+
f"This is probably because the resource '{value}' could not be created. "
|
|
189
|
+
f"See {WARNINGS_SAVEPATH} for more information."
|
|
190
|
+
)
|
|
191
|
+
raise BaseError(msg)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _make_date_value_graph(
|
|
195
|
+
val: ProcessedDate,
|
|
196
|
+
res_node: BNode | URIRef,
|
|
197
|
+
) -> Graph:
|
|
198
|
+
val_bn = BNode()
|
|
199
|
+
date = val.value
|
|
200
|
+
g = _add_optional_triples(val_bn, val.permissions, val.comment)
|
|
201
|
+
g.add((res_node, URIRef(val.prop_iri), val_bn))
|
|
202
|
+
g.add((val_bn, RDF.type, KNORA_API.DateValue))
|
|
203
|
+
if cal := date.calendar:
|
|
204
|
+
g.add((val_bn, KNORA_API.dateValueHasCalendar, Literal(cal.value, datatype=XSD.string)))
|
|
205
|
+
g += _make_single_date_graph(val_bn, date.start, StartEnd.START)
|
|
206
|
+
if date.end:
|
|
207
|
+
g += _make_single_date_graph(val_bn, date.end, StartEnd.END)
|
|
208
|
+
return g
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _make_single_date_graph(val_bn: BNode, date: SingleDate, start_end: StartEnd) -> Graph:
|
|
212
|
+
def get_prop(precision: DayMonthYearEra) -> URIRef:
|
|
213
|
+
return KNORA_API[f"dateValueHas{start_end.value}{precision.value}"]
|
|
214
|
+
|
|
215
|
+
g = Graph()
|
|
216
|
+
if yr := date.year:
|
|
217
|
+
g.add((val_bn, get_prop(DayMonthYearEra.YEAR), Literal(yr, datatype=XSD.integer)))
|
|
218
|
+
if mnt := date.month:
|
|
219
|
+
g.add((val_bn, get_prop(DayMonthYearEra.MONTH), Literal(mnt, datatype=XSD.integer)))
|
|
220
|
+
if day := date.day:
|
|
221
|
+
g.add((val_bn, get_prop(DayMonthYearEra.DAY), Literal(day, datatype=XSD.integer)))
|
|
222
|
+
if era := date.era:
|
|
223
|
+
g.add((val_bn, get_prop(DayMonthYearEra.ERA), Literal(era.value, datatype=XSD.string)))
|
|
224
|
+
return g
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _make_interval_value_graph(
|
|
228
|
+
val: ProcessedInterval,
|
|
229
|
+
res_node: BNode | URIRef,
|
|
230
|
+
) -> Graph:
|
|
231
|
+
val_bn = BNode()
|
|
232
|
+
g = _add_optional_triples(val_bn, val.permissions, val.comment)
|
|
233
|
+
g.add((res_node, URIRef(val.prop_iri), val_bn))
|
|
234
|
+
g.add((val_bn, RDF.type, KNORA_API.IntervalValue))
|
|
235
|
+
g.add((val_bn, KNORA_API.intervalValueHasStart, Literal(val.value.start, datatype=XSD.decimal)))
|
|
236
|
+
g.add((val_bn, KNORA_API.intervalValueHasEnd, Literal(val.value.end, datatype=XSD.decimal)))
|
|
237
|
+
return g
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def make_richtext_value_graph(
|
|
241
|
+
val: ProcessedRichtext,
|
|
242
|
+
val_node: BNode | URIRef,
|
|
243
|
+
res_node: BNode | URIRef,
|
|
244
|
+
iri_resolver: IriResolver,
|
|
245
|
+
) -> Graph:
|
|
246
|
+
"""
|
|
247
|
+
Creates an rdflib graph for a richtext value.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
val: Richtext value
|
|
251
|
+
val_node: IRI or blank node of the value
|
|
252
|
+
res_node: IRI or blank node of the resource
|
|
253
|
+
iri_resolver: id to IRI resolver
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Graph
|
|
257
|
+
"""
|
|
258
|
+
g = _make_base_value_graph(val=val, val_node=val_node, prop_type_info=RICHTEXT_PROP_TYPE_INFO, res_node=res_node)
|
|
259
|
+
val_str = prepare_richtext_string_for_upload(val.value.xmlstr, iri_resolver)
|
|
260
|
+
g.add((val_node, RICHTEXT_PROP_TYPE_INFO.knora_prop, Literal(val_str, datatype=XSD.string)))
|
|
261
|
+
g.add((val_node, KNORA_API.textValueHasMapping, URIRef("http://rdfh.ch/standoff/mappings/StandardMapping")))
|
|
262
|
+
return g
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class BitstreamInfo:
|
|
10
|
+
"""
|
|
11
|
+
Represents a bitstream object,
|
|
12
|
+
consisting of its file name on the local file system,
|
|
13
|
+
the internal file name assigned by the ingest service
|
|
14
|
+
and optionally its permissions.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
internal_file_name: str
|
|
18
|
+
permissions: Permissions | None = None
|
|
@@ -2,34 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
|
|
5
|
-
import regex
|
|
6
|
-
|
|
7
|
-
from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
|
|
8
|
-
from dsp_tools.models.exceptions import BaseError
|
|
9
|
-
|
|
10
5
|
|
|
11
6
|
@dataclass
|
|
12
7
|
class FormattedTextValue:
|
|
13
8
|
"""Represents a formatted text value with standard standoff markup"""
|
|
14
9
|
|
|
15
10
|
xmlstr: str
|
|
16
|
-
|
|
17
|
-
def as_xml(self) -> str:
|
|
18
|
-
"""Returns the formatted text value as XML (with XML declaration and wrapped in a <text> element)"""
|
|
19
|
-
return f'<?xml version="1.0" encoding="UTF-8"?>\n<text>{self.xmlstr}</text>'
|
|
20
|
-
|
|
21
|
-
def find_internal_ids(self) -> set[str]:
|
|
22
|
-
"""Returns a set of all internal ids found in the text value"""
|
|
23
|
-
return set(regex.findall(pattern='href="IRI:(.*?):IRI"', string=self.xmlstr))
|
|
24
|
-
|
|
25
|
-
def with_iris(self, iri_resolver: IriResolver) -> FormattedTextValue:
|
|
26
|
-
"""
|
|
27
|
-
Returns a copy of this object, where all internal ids are replaced with iris according to the provided mapping.
|
|
28
|
-
"""
|
|
29
|
-
s = self.xmlstr
|
|
30
|
-
for internal_id in self.find_internal_ids():
|
|
31
|
-
if iri := iri_resolver.get(internal_id):
|
|
32
|
-
s = s.replace(f'href="IRI:{internal_id}:IRI"', f'href="{iri}"')
|
|
33
|
-
else:
|
|
34
|
-
raise BaseError(f"Internal ID {internal_id} could not be resolved to an IRI")
|
|
35
|
-
return FormattedTextValue(s)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import urllib
|
|
2
4
|
from dataclasses import dataclass
|
|
3
5
|
from dataclasses import field
|
|
6
|
+
from http import HTTPStatus
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
from typing import Protocol
|
|
6
9
|
|
|
@@ -10,16 +13,15 @@ from requests import Session
|
|
|
10
13
|
from requests.adapters import HTTPAdapter
|
|
11
14
|
from requests.adapters import Retry
|
|
12
15
|
|
|
13
|
-
from dsp_tools.
|
|
14
|
-
from dsp_tools.commands.xmlupload.models.
|
|
15
|
-
from dsp_tools.commands.xmlupload.models.
|
|
16
|
-
from dsp_tools.
|
|
17
|
-
from dsp_tools.
|
|
18
|
-
from dsp_tools.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
STATUS_INTERNAL_SERVER_ERROR = 500
|
|
16
|
+
from dsp_tools.clients.authentication_client import AuthenticationClient
|
|
17
|
+
from dsp_tools.commands.xmlupload.models.bitstream_info import BitstreamInfo
|
|
18
|
+
from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedFileValue
|
|
19
|
+
from dsp_tools.error.exceptions import BadCredentialsError
|
|
20
|
+
from dsp_tools.error.exceptions import InvalidIngestFileNameError
|
|
21
|
+
from dsp_tools.error.exceptions import PermanentConnectionError
|
|
22
|
+
from dsp_tools.utils.request_utils import RequestParameters
|
|
23
|
+
from dsp_tools.utils.request_utils import log_request
|
|
24
|
+
from dsp_tools.utils.request_utils import log_response
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
@dataclass(frozen=True)
|
|
@@ -32,20 +34,11 @@ class IngestResponse:
|
|
|
32
34
|
class AssetClient(Protocol):
|
|
33
35
|
"""Protocol for asset handling clients."""
|
|
34
36
|
|
|
35
|
-
def get_bitstream_info(
|
|
36
|
-
|
|
37
|
-
bitstream: XMLBitstream,
|
|
38
|
-
permissions_lookup: dict[str, Permissions],
|
|
39
|
-
res_label: str,
|
|
40
|
-
res_id: str,
|
|
41
|
-
) -> tuple[bool, None | BitstreamInfo]:
|
|
42
|
-
"""Uploads the file to the ingest server if applicable, and returns the BitstreamInfo.
|
|
37
|
+
def get_bitstream_info(self, file_info: ProcessedFileValue) -> BitstreamInfo | None:
|
|
38
|
+
"""Uploads the file to the ingest server if applicable, and returns the upload results.
|
|
43
39
|
|
|
44
40
|
Args:
|
|
45
|
-
|
|
46
|
-
permissions_lookup: The permissions lookup.
|
|
47
|
-
res_label: The resource label (for error message in failure case).
|
|
48
|
-
res_id: The resource ID (for error message in failure case).
|
|
41
|
+
file_info: Information required for ingesting an asset
|
|
49
42
|
"""
|
|
50
43
|
|
|
51
44
|
|
|
@@ -54,7 +47,7 @@ class DspIngestClientLive(AssetClient):
|
|
|
54
47
|
"""Client for uploading assets to the DSP-Ingest."""
|
|
55
48
|
|
|
56
49
|
dsp_ingest_url: str
|
|
57
|
-
|
|
50
|
+
authentication_client: AuthenticationClient
|
|
58
51
|
shortcode: str
|
|
59
52
|
imgdir: str
|
|
60
53
|
session: Session = field(init=False)
|
|
@@ -68,7 +61,7 @@ class DspIngestClientLive(AssetClient):
|
|
|
68
61
|
connect=retries,
|
|
69
62
|
backoff_factor=0.3,
|
|
70
63
|
allowed_methods=None, # means all methods
|
|
71
|
-
status_forcelist=[
|
|
64
|
+
status_forcelist=[HTTPStatus.INTERNAL_SERVER_ERROR.value],
|
|
72
65
|
)
|
|
73
66
|
adapter = HTTPAdapter(max_retries=retry)
|
|
74
67
|
self.session.mount("http://", adapter)
|
|
@@ -83,6 +76,10 @@ class DspIngestClientLive(AssetClient):
|
|
|
83
76
|
After all retry attempts are exhausted it will raise exceptions if the upload failed.
|
|
84
77
|
The http status code is also checked and if it is not 200, a PermanentConnectionError is raised.
|
|
85
78
|
|
|
79
|
+
The load balancer on DSP servers currently has a timeout of 10m,
|
|
80
|
+
so we need to use a slightly shorter timeout of 9m.
|
|
81
|
+
See https://linear.app/dasch/issue/INFRA-847/increase-traefik-readtimeout
|
|
82
|
+
|
|
86
83
|
Args:
|
|
87
84
|
filepath: Path to the file to ingest, could be either absolute or relative.
|
|
88
85
|
|
|
@@ -95,63 +92,52 @@ class DspIngestClientLive(AssetClient):
|
|
|
95
92
|
"""
|
|
96
93
|
filename = urllib.parse.quote(filepath.name)
|
|
97
94
|
url = f"{self.dsp_ingest_url}/projects/{self.shortcode}/assets/ingest/{filename}"
|
|
98
|
-
headers = {
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
headers = {
|
|
96
|
+
"Authorization": f"Bearer {self.authentication_client.get_token()}",
|
|
97
|
+
"Content-Type": "application/octet-stream",
|
|
98
|
+
}
|
|
99
|
+
timeout = 9 * 60
|
|
100
|
+
params = RequestParameters(method="POST", url=url, timeout=timeout, headers=headers)
|
|
101
101
|
with open(filepath, "rb") as binary_io:
|
|
102
102
|
try:
|
|
103
|
-
|
|
103
|
+
log_request(params)
|
|
104
104
|
res = self.session.post(
|
|
105
|
-
url=url,
|
|
106
|
-
headers=headers,
|
|
105
|
+
url=params.url,
|
|
106
|
+
headers=params.headers,
|
|
107
107
|
data=binary_io,
|
|
108
|
-
timeout=timeout,
|
|
108
|
+
timeout=params.timeout,
|
|
109
109
|
)
|
|
110
|
-
|
|
111
|
-
if res.status_code == STATUS_OK:
|
|
112
|
-
return IngestResponse(internal_filename=res.json()["internalFilename"])
|
|
113
|
-
elif res.status_code == STATUS_UNAUTHORIZED:
|
|
114
|
-
raise BadCredentialsError("Bad credentials")
|
|
115
|
-
else:
|
|
116
|
-
user_msg = f"{err} See {WARNINGS_SAVEPATH} for more information."
|
|
117
|
-
print(user_msg)
|
|
118
|
-
log_msg = f"{err}. Response status code {res.status_code} '{res.text}'"
|
|
119
|
-
logger.error(log_msg)
|
|
120
|
-
raise PermanentConnectionError(log_msg)
|
|
110
|
+
log_response(res)
|
|
121
111
|
except requests.exceptions.RequestException as e:
|
|
122
|
-
raise PermanentConnectionError(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
112
|
+
raise PermanentConnectionError() from e
|
|
113
|
+
|
|
114
|
+
if res.ok:
|
|
115
|
+
return IngestResponse(internal_filename=res.json()["internalFilename"])
|
|
116
|
+
elif res.status_code == HTTPStatus.FORBIDDEN:
|
|
117
|
+
raise BadCredentialsError("Only SystemAdmins and ProjectAdmins can upload assets.")
|
|
118
|
+
elif res.status_code == HTTPStatus.BAD_REQUEST and res.text == "Invalid value for: path parameter filename":
|
|
119
|
+
raise InvalidIngestFileNameError()
|
|
120
|
+
else:
|
|
121
|
+
raise PermanentConnectionError()
|
|
122
|
+
|
|
123
|
+
def get_bitstream_info(self, file_info: ProcessedFileValue) -> BitstreamInfo | None:
|
|
124
|
+
"""Uploads a file to the ingest server and returns the upload results."""
|
|
132
125
|
try:
|
|
133
|
-
res = self._ingest(Path(self.imgdir) / Path(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
res = self._ingest(Path(self.imgdir) / Path(file_info.value))
|
|
127
|
+
logger.info(f"Uploaded file '{file_info.value}'")
|
|
128
|
+
return BitstreamInfo(res.internal_filename, file_info.metadata.permissions)
|
|
129
|
+
except InvalidIngestFileNameError:
|
|
130
|
+
msg = f"Invalid filename: Unable to upload file '{file_info.value}' of resource '{file_info.res_id}'"
|
|
138
131
|
except PermanentConnectionError:
|
|
139
|
-
msg = f"Unable to upload file '{
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
msg = f"Unable to upload file '{file_info.value}' of resource '{file_info.res_id}'"
|
|
133
|
+
logger.error(msg)
|
|
134
|
+
return None
|
|
142
135
|
|
|
143
136
|
|
|
144
137
|
@dataclass(frozen=True)
|
|
145
138
|
class BulkIngestedAssetClient(AssetClient):
|
|
146
139
|
"""Client for handling media info, if the assets were bulk ingested previously."""
|
|
147
140
|
|
|
148
|
-
def get_bitstream_info(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
permissions_lookup: dict[str, Permissions],
|
|
152
|
-
res_label: str, # noqa: ARG002
|
|
153
|
-
res_id: str, # noqa: ARG002
|
|
154
|
-
) -> tuple[bool, BitstreamInfo | None]:
|
|
155
|
-
"""Returns the BitstreamInfo of the already ingested file based on the `XMLBitstream.value`."""
|
|
156
|
-
permissions = permissions_lookup.get(bitstream.permissions) if bitstream.permissions else None
|
|
157
|
-
return True, BitstreamInfo(bitstream.value, bitstream.value, permissions)
|
|
141
|
+
def get_bitstream_info(self, file_info: ProcessedFileValue) -> BitstreamInfo:
|
|
142
|
+
"""Returns the BitstreamInfo of the already ingested file based on the `ProcessedFileValue.value`."""
|
|
143
|
+
return BitstreamInfo(file_info.value, file_info.metadata.permissions)
|
|
@@ -29,7 +29,6 @@ class IIIFUriProblem:
|
|
|
29
29
|
regex_has_passed: bool
|
|
30
30
|
status_code: int | None = None
|
|
31
31
|
raised_exception_name: str | None = None
|
|
32
|
-
original_text: str | None = None
|
|
33
32
|
|
|
34
33
|
def get_msg(self) -> str:
|
|
35
34
|
"""Get a message describing the problem with the IIIF URI."""
|
|
@@ -43,24 +42,17 @@ class IIIFUriProblem:
|
|
|
43
42
|
elif self.status_code is not None:
|
|
44
43
|
msg.extend(self._bad_status_code_msg())
|
|
45
44
|
else:
|
|
46
|
-
msg.
|
|
45
|
+
msg.append(self._exception_msg())
|
|
47
46
|
return list_separator.join(msg)
|
|
48
47
|
|
|
49
|
-
def _exception_msg(self) ->
|
|
50
|
-
return
|
|
51
|
-
f"An error occurred during the network call: {self.raised_exception_name}",
|
|
52
|
-
f"Error message: {self.original_text}",
|
|
53
|
-
]
|
|
48
|
+
def _exception_msg(self) -> str:
|
|
49
|
+
return f"An error occurred during the network call: {self.raised_exception_name}"
|
|
54
50
|
|
|
55
51
|
def _bad_status_code_msg(self) -> list[str]:
|
|
56
|
-
return [
|
|
57
|
-
"The server did not respond as expected.",
|
|
58
|
-
f"Status code: {self.status_code}",
|
|
59
|
-
f"Response text: {self.original_text}",
|
|
60
|
-
]
|
|
52
|
+
return ["The server did not respond as expected.", f"Status code: {self.status_code}"]
|
|
61
53
|
|
|
62
54
|
def _good_status_code_bad_regex_msg(self) -> list[str]:
|
|
63
55
|
return [
|
|
64
|
-
"
|
|
65
|
-
"Please contact the dsp-tools development team with this information.",
|
|
56
|
+
"Although the IIIF-server responded as expected, this URI did not pass validation.",
|
|
57
|
+
"Please contact the dsp-tools development team (at support@dasch.swiss) with this information.",
|
|
66
58
|
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from rdflib import URIRef
|
|
6
|
+
|
|
7
|
+
from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
|
|
8
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class XmlReferenceLookups:
|
|
13
|
+
permissions: dict[str, Permissions]
|
|
14
|
+
listnodes: dict[tuple[str, str], str]
|
|
15
|
+
authorships: dict[str, list[str]]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class IRILookups:
|
|
20
|
+
project_iri: URIRef
|
|
21
|
+
id_to_iri: IriResolver
|
|
@@ -3,9 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from enum import unique
|
|
5
5
|
from typing import Optional
|
|
6
|
-
from typing import Union
|
|
7
|
-
|
|
8
|
-
import regex
|
|
9
6
|
|
|
10
7
|
|
|
11
8
|
@unique
|
|
@@ -33,21 +30,6 @@ class Permissions:
|
|
|
33
30
|
def __init__(self, permissions: Optional[dict[PermissionValue, list[str]]] = None):
|
|
34
31
|
self._permissions = permissions or {}
|
|
35
32
|
|
|
36
|
-
def __getitem__(self, key: PermissionValue) -> Union[list[str], None]:
|
|
37
|
-
return self._permissions.get(key)
|
|
38
|
-
|
|
39
|
-
def __setitem__(self, key: PermissionValue, value: list[str]) -> None:
|
|
40
|
-
self._permissions[key] = value
|
|
41
|
-
|
|
42
|
-
def __delitem__(self, key: PermissionValue) -> None:
|
|
43
|
-
del self._permissions[key]
|
|
44
|
-
|
|
45
|
-
def __missing__(self, key: PermissionValue) -> None:
|
|
46
|
-
return None
|
|
47
|
-
|
|
48
|
-
def __contains__(self, key: PermissionValue) -> bool:
|
|
49
|
-
return key in self._permissions
|
|
50
|
-
|
|
51
33
|
def __str__(self) -> str:
|
|
52
34
|
tmpstr = ""
|
|
53
35
|
for permission, groups in self._permissions.items():
|
|
@@ -61,24 +43,3 @@ class Permissions:
|
|
|
61
43
|
self._permissions[key] = [val]
|
|
62
44
|
else:
|
|
63
45
|
self._permissions[key].append(val)
|
|
64
|
-
|
|
65
|
-
def toJsonLdObj(self) -> str:
|
|
66
|
-
tmpstr = ""
|
|
67
|
-
for permission, groups in self._permissions.items():
|
|
68
|
-
if tmpstr:
|
|
69
|
-
tmpstr += "|"
|
|
70
|
-
tmpstr += f"{permission!s} " + ",".join(groups)
|
|
71
|
-
return tmpstr
|
|
72
|
-
|
|
73
|
-
@classmethod
|
|
74
|
-
def fromString(cls, permstr: str) -> Permissions:
|
|
75
|
-
tmpstr = permstr.split("|")
|
|
76
|
-
permissions: dict[PermissionValue, list[str]] = {}
|
|
77
|
-
for s in tmpstr:
|
|
78
|
-
key, *vals = regex.split("[\\s,]+", s)
|
|
79
|
-
permissions[PermissionValue[key]] = vals
|
|
80
|
-
return cls(permissions)
|
|
81
|
-
|
|
82
|
-
@property
|
|
83
|
-
def permissions(self) -> Union[dict[PermissionValue, list[str]], None]:
|
|
84
|
-
return self._permissions
|
|
@@ -4,8 +4,8 @@ from lxml import etree
|
|
|
4
4
|
|
|
5
5
|
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
6
6
|
from dsp_tools.commands.xmlupload.models.permission import PermissionValue
|
|
7
|
-
from dsp_tools.
|
|
8
|
-
from dsp_tools.
|
|
7
|
+
from dsp_tools.error.exceptions import XmlUploadError
|
|
8
|
+
from dsp_tools.legacy_models.projectContext import ProjectContext
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class XmlPermission:
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
6
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import KnoraValueType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ProcessedFileMetadata:
|
|
11
|
+
license_iri: str
|
|
12
|
+
copyright_holder: str
|
|
13
|
+
authorships: list[str]
|
|
14
|
+
permissions: Permissions | None = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ProcessedFileValue:
|
|
19
|
+
value: str
|
|
20
|
+
file_type: KnoraValueType
|
|
21
|
+
metadata: ProcessedFileMetadata
|
|
22
|
+
res_id: str
|
|
23
|
+
res_label: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ProcessedIIIFUri:
|
|
28
|
+
value: str
|
|
29
|
+
metadata: ProcessedFileMetadata
|