dsp-tools 0.9.13__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 +5 -0
- dsp_tools/cli/args.py +47 -0
- dsp_tools/cli/call_action.py +85 -0
- 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 +479 -0
- dsp_tools/cli/entry_point.py +322 -0
- 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/clients/connection.py +35 -0
- 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 +321 -0
- dsp_tools/commands/excel2json/lists/__init__.py +0 -0
- dsp_tools/commands/excel2json/lists/compliance_checks.py +292 -0
- dsp_tools/commands/excel2json/lists/make_lists.py +247 -0
- dsp_tools/commands/excel2json/lists/models/__init__.py +0 -0
- dsp_tools/commands/excel2json/lists/models/deserialise.py +30 -0
- dsp_tools/commands/excel2json/lists/models/input_error.py +216 -0
- dsp_tools/commands/excel2json/lists/models/serialise.py +57 -0
- dsp_tools/commands/excel2json/lists/utils.py +81 -0
- dsp_tools/commands/excel2json/models/__init__.py +0 -0
- dsp_tools/commands/excel2json/models/input_error.py +416 -0
- dsp_tools/commands/excel2json/models/json_header.py +175 -0
- dsp_tools/commands/excel2json/models/list_node_name.py +16 -0
- dsp_tools/commands/excel2json/models/ontology.py +76 -0
- dsp_tools/commands/excel2json/old_lists.py +328 -0
- dsp_tools/commands/excel2json/project.py +280 -0
- dsp_tools/commands/excel2json/properties.py +370 -0
- dsp_tools/commands/excel2json/resources.py +336 -0
- dsp_tools/commands/excel2json/utils.py +352 -0
- dsp_tools/commands/excel2xml/__init__.py +7 -0
- dsp_tools/commands/excel2xml/excel2xml_cli.py +523 -0
- dsp_tools/commands/excel2xml/excel2xml_lib.py +1953 -0
- dsp_tools/commands/excel2xml/propertyelement.py +47 -0
- dsp_tools/commands/get/__init__.py +0 -0
- dsp_tools/commands/get/get.py +166 -0
- dsp_tools/commands/get/get_permissions.py +257 -0
- dsp_tools/commands/get/get_permissions_legacy.py +89 -0
- dsp_tools/commands/get/legacy_models/__init__.py +0 -0
- dsp_tools/commands/get/legacy_models/context.py +318 -0
- dsp_tools/commands/get/legacy_models/group.py +241 -0
- dsp_tools/commands/get/legacy_models/helpers.py +47 -0
- dsp_tools/commands/get/legacy_models/listnode.py +390 -0
- dsp_tools/commands/get/legacy_models/model.py +12 -0
- dsp_tools/commands/get/legacy_models/ontology.py +324 -0
- dsp_tools/commands/get/legacy_models/project.py +366 -0
- dsp_tools/commands/get/legacy_models/propertyclass.py +417 -0
- dsp_tools/commands/get/legacy_models/resourceclass.py +676 -0
- dsp_tools/commands/get/legacy_models/user.py +438 -0
- dsp_tools/commands/get/models/__init__.py +0 -0
- dsp_tools/commands/get/models/permissions_models.py +10 -0
- dsp_tools/commands/id2iri.py +258 -0
- dsp_tools/commands/ingest_xmlupload/__init__.py +0 -0
- dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +178 -0
- dsp_tools/commands/ingest_xmlupload/create_resources/__init__.py +0 -0
- dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +69 -0
- dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +166 -0
- dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +121 -0
- dsp_tools/commands/ingest_xmlupload/ingest_files/__init__.py +0 -0
- dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +64 -0
- dsp_tools/commands/ingest_xmlupload/upload_files/__init__.py +0 -0
- dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +20 -0
- dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +57 -0
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +66 -0
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +67 -0
- dsp_tools/commands/resume_xmlupload/__init__.py +0 -0
- dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +96 -0
- dsp_tools/commands/start_stack.py +428 -0
- 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/validate_data/sparql/cardinality_shacl.py +209 -0
- 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/__init__.py +0 -0
- dsp_tools/commands/xmlupload/iri_resolver.py +21 -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/__init__.py +0 -0
- dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
- dsp_tools/commands/xmlupload/models/formatted_text_value.py +10 -0
- dsp_tools/commands/xmlupload/models/ingest.py +143 -0
- dsp_tools/commands/xmlupload/models/input_problems.py +58 -0
- dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
- dsp_tools/commands/xmlupload/models/permission.py +45 -0
- dsp_tools/commands/xmlupload/models/permissions_parsed.py +93 -0
- 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 +14 -0
- dsp_tools/commands/xmlupload/models/upload_state.py +20 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/ark2iri.py +55 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/iiif_uri_validator.py +50 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/list_client.py +120 -0
- 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 +25 -0
- dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
- dsp_tools/commands/xmlupload/stash/__init__.py +0 -0
- dsp_tools/commands/xmlupload/stash/analyse_circular_reference_graph.py +236 -0
- dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
- dsp_tools/commands/xmlupload/stash/graph_models.py +87 -0
- dsp_tools/commands/xmlupload/stash/stash_circular_references.py +68 -0
- dsp_tools/commands/xmlupload/stash/stash_models.py +109 -0
- dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +106 -0
- dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +196 -0
- dsp_tools/commands/xmlupload/upload_config.py +76 -0
- dsp_tools/commands/xmlupload/write_diagnostic_info.py +27 -0
- dsp_tools/commands/xmlupload/xmlupload.py +516 -0
- dsp_tools/config/__init__.py +0 -0
- dsp_tools/config/logger_config.py +69 -0
- dsp_tools/config/warnings_config.py +32 -0
- 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/legacy_models/datetimestamp.py +81 -0
- dsp_tools/legacy_models/langstring.py +253 -0
- dsp_tools/legacy_models/projectContext.py +49 -0
- dsp_tools/py.typed +0 -0
- dsp_tools/resources/schema/data.xsd +648 -0
- dsp_tools/resources/schema/lists-only.json +72 -0
- dsp_tools/resources/schema/project.json +1258 -0
- dsp_tools/resources/schema/properties-only.json +874 -0
- dsp_tools/resources/schema/resources-only.json +140 -0
- dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
- dsp_tools/resources/start-stack/docker-compose.override.yml +11 -0
- dsp_tools/resources/start-stack/docker-compose.yml +88 -0
- 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/__init__.py +0 -0
- dsp_tools/utils/ansi_colors.py +32 -0
- dsp_tools/utils/data_formats/__init__.py +0 -0
- dsp_tools/utils/data_formats/date_util.py +166 -0
- dsp_tools/utils/data_formats/iri_util.py +30 -0
- dsp_tools/utils/data_formats/shared.py +81 -0
- dsp_tools/utils/data_formats/uri_util.py +76 -0
- 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/__init__.py +0 -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 +1542 -0
- 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/internal/migration_metadata.py +55 -0
- 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 +348 -0
- dsp_tools/xmllib/value_checkers.py +434 -0
- dsp_tools/xmllib/value_converters.py +777 -0
- 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-18.3.0.post13.dist-info/entry_points.txt +3 -0
- dsp_tools-0.9.13.dist-info/LICENSE +0 -674
- dsp_tools-0.9.13.dist-info/METADATA +0 -144
- dsp_tools-0.9.13.dist-info/RECORD +0 -71
- dsp_tools-0.9.13.dist-info/WHEEL +0 -5
- dsp_tools-0.9.13.dist-info/entry_points.txt +0 -3
- dsp_tools-0.9.13.dist-info/top_level.txt +0 -1
- dsplib/models/connection.py +0 -272
- dsplib/models/group.py +0 -296
- dsplib/models/helpers.py +0 -505
- dsplib/models/langstring.py +0 -277
- dsplib/models/listnode.py +0 -578
- dsplib/models/model.py +0 -20
- dsplib/models/ontology.py +0 -448
- dsplib/models/permission.py +0 -112
- dsplib/models/project.py +0 -547
- dsplib/models/propertyclass.py +0 -505
- dsplib/models/resource.py +0 -366
- dsplib/models/resourceclass.py +0 -810
- dsplib/models/sipi.py +0 -30
- dsplib/models/user.py +0 -731
- dsplib/models/value.py +0 -1000
- dsplib/utils/knora-data-schema.xsd +0 -454
- dsplib/utils/knora-schema-lists.json +0 -83
- dsplib/utils/knora-schema.json +0 -434
- dsplib/utils/onto_commons.py +0 -24
- dsplib/utils/onto_create_lists.py +0 -73
- dsplib/utils/onto_create_ontology.py +0 -442
- dsplib/utils/onto_get.py +0 -58
- dsplib/utils/onto_validate.py +0 -33
- dsplib/utils/xml_upload.py +0 -539
- dsplib/widgets/doublepassword.py +0 -80
- knora/MLS-import-libraries.py +0 -84
- knora/dsp_tools.py +0 -96
- knora/dsplib/models/connection.py +0 -272
- knora/dsplib/models/group.py +0 -296
- knora/dsplib/models/helpers.py +0 -506
- knora/dsplib/models/langstring.py +0 -277
- knora/dsplib/models/listnode.py +0 -578
- knora/dsplib/models/model.py +0 -20
- knora/dsplib/models/ontology.py +0 -448
- knora/dsplib/models/permission.py +0 -112
- knora/dsplib/models/project.py +0 -583
- knora/dsplib/models/propertyclass.py +0 -505
- knora/dsplib/models/resource.py +0 -416
- knora/dsplib/models/resourceclass.py +0 -811
- knora/dsplib/models/sipi.py +0 -35
- knora/dsplib/models/user.py +0 -731
- knora/dsplib/models/value.py +0 -1000
- knora/dsplib/utils/knora-data-schema.xsd +0 -464
- knora/dsplib/utils/knora-schema-lists.json +0 -83
- knora/dsplib/utils/knora-schema.json +0 -444
- knora/dsplib/utils/onto_commons.py +0 -24
- knora/dsplib/utils/onto_create_lists.py +0 -73
- knora/dsplib/utils/onto_create_ontology.py +0 -451
- knora/dsplib/utils/onto_get.py +0 -58
- knora/dsplib/utils/onto_validate.py +0 -33
- knora/dsplib/utils/xml_upload.py +0 -540
- knora/dsplib/widgets/doublepassword.py +0 -80
- knora/knora.py +0 -2108
- knora/test.py +0 -99
- knora/testit.py +0 -76
- knora/xml2knora.py +0 -633
- {dsplib → dsp_tools/cli}/__init__.py +0 -0
- {dsplib/models → dsp_tools/clients}/__init__.py +0 -0
- {dsplib/utils → dsp_tools/commands}/__init__.py +0 -0
- {dsplib/widgets → dsp_tools/commands/create}/__init__.py +0 -0
- {knora → dsp_tools/commands/create/create_on_server}/__init__.py +0 -0
- {knora/dsplib → dsp_tools/commands/create/models}/__init__.py +0 -0
- {knora/dsplib/models → dsp_tools/commands/create/parsing}/__init__.py +0 -0
- {knora/dsplib/utils → dsp_tools/commands/create/serialisation}/__init__.py +0 -0
- {knora/dsplib/widgets → dsp_tools/commands/excel2json}/__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
|
|
File without changes
|
|
@@ -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
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import urllib
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from dataclasses import field
|
|
6
|
+
from http import HTTPStatus
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Protocol
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
from loguru import logger
|
|
12
|
+
from requests import Session
|
|
13
|
+
from requests.adapters import HTTPAdapter
|
|
14
|
+
from requests.adapters import Retry
|
|
15
|
+
|
|
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
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class IngestResponse:
|
|
29
|
+
"""Wrapper around `internal_filename`"""
|
|
30
|
+
|
|
31
|
+
internal_filename: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AssetClient(Protocol):
|
|
35
|
+
"""Protocol for asset handling clients."""
|
|
36
|
+
|
|
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.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
file_info: Information required for ingesting an asset
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class DspIngestClientLive(AssetClient):
|
|
47
|
+
"""Client for uploading assets to the DSP-Ingest."""
|
|
48
|
+
|
|
49
|
+
dsp_ingest_url: str
|
|
50
|
+
authentication_client: AuthenticationClient
|
|
51
|
+
shortcode: str
|
|
52
|
+
imgdir: str
|
|
53
|
+
session: Session = field(init=False)
|
|
54
|
+
|
|
55
|
+
def __post_init__(self) -> None:
|
|
56
|
+
retries = 6
|
|
57
|
+
self.session = Session()
|
|
58
|
+
retry = Retry(
|
|
59
|
+
total=retries,
|
|
60
|
+
read=retries,
|
|
61
|
+
connect=retries,
|
|
62
|
+
backoff_factor=0.3,
|
|
63
|
+
allowed_methods=None, # means all methods
|
|
64
|
+
status_forcelist=[HTTPStatus.INTERNAL_SERVER_ERROR.value],
|
|
65
|
+
)
|
|
66
|
+
adapter = HTTPAdapter(max_retries=retry)
|
|
67
|
+
self.session.mount("http://", adapter)
|
|
68
|
+
self.session.mount("https://", adapter)
|
|
69
|
+
|
|
70
|
+
def _ingest(self, filepath: Path) -> IngestResponse:
|
|
71
|
+
"""Uploads a file to the ingest server and returns the IngestResponse.
|
|
72
|
+
|
|
73
|
+
This function sends a POST request to the ingest server with the file to upload.
|
|
74
|
+
It will retry the request 6 times in case of a connection, timeout or internal server error.
|
|
75
|
+
|
|
76
|
+
After all retry attempts are exhausted it will raise exceptions if the upload failed.
|
|
77
|
+
The http status code is also checked and if it is not 200, a PermanentConnectionError is raised.
|
|
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
|
+
|
|
83
|
+
Args:
|
|
84
|
+
filepath: Path to the file to ingest, could be either absolute or relative.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
BadCredentialsError: If the credentials are invalid.
|
|
88
|
+
PermanentConnectionError: If the connection fails.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
IngestResponse: The internal filename of the uploaded file.
|
|
92
|
+
"""
|
|
93
|
+
filename = urllib.parse.quote(filepath.name)
|
|
94
|
+
url = f"{self.dsp_ingest_url}/projects/{self.shortcode}/assets/ingest/{filename}"
|
|
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
|
+
with open(filepath, "rb") as binary_io:
|
|
102
|
+
try:
|
|
103
|
+
log_request(params)
|
|
104
|
+
res = self.session.post(
|
|
105
|
+
url=params.url,
|
|
106
|
+
headers=params.headers,
|
|
107
|
+
data=binary_io,
|
|
108
|
+
timeout=params.timeout,
|
|
109
|
+
)
|
|
110
|
+
log_response(res)
|
|
111
|
+
except requests.exceptions.RequestException as e:
|
|
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."""
|
|
125
|
+
try:
|
|
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}'"
|
|
131
|
+
except PermanentConnectionError:
|
|
132
|
+
msg = f"Unable to upload file '{file_info.value}' of resource '{file_info.res_id}'"
|
|
133
|
+
logger.error(msg)
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass(frozen=True)
|
|
138
|
+
class BulkIngestedAssetClient(AssetClient):
|
|
139
|
+
"""Client for handling media info, if the assets were bulk ingested previously."""
|
|
140
|
+
|
|
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)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
list_separator = "\n - "
|
|
6
|
+
medium_separator = "\n----------------------------\n"
|
|
7
|
+
|
|
8
|
+
HTTP_OK = 200
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class AllIIIFUriProblems:
|
|
13
|
+
"""Information about all problems with IIIF URIs."""
|
|
14
|
+
|
|
15
|
+
problems: list[IIIFUriProblem]
|
|
16
|
+
|
|
17
|
+
def get_msg(self) -> str:
|
|
18
|
+
"""Get a message describing all problems with the IIIF URIs."""
|
|
19
|
+
msg = "There were problems with the following IIIF URI(s):"
|
|
20
|
+
all_msg = [problem.get_msg() for problem in self.problems]
|
|
21
|
+
return msg + medium_separator + medium_separator.join(all_msg)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class IIIFUriProblem:
|
|
26
|
+
"""Information about a problem with an IIIF URI."""
|
|
27
|
+
|
|
28
|
+
uri: str
|
|
29
|
+
regex_has_passed: bool
|
|
30
|
+
status_code: int | None = None
|
|
31
|
+
raised_exception_name: str | None = None
|
|
32
|
+
|
|
33
|
+
def get_msg(self) -> str:
|
|
34
|
+
"""Get a message describing the problem with the IIIF URI."""
|
|
35
|
+
msg = [f"URI: {self.uri}"]
|
|
36
|
+
if self.regex_has_passed:
|
|
37
|
+
msg.append("Passed the internal regex check.")
|
|
38
|
+
else:
|
|
39
|
+
msg.append("Did not pass the internal regex check.")
|
|
40
|
+
if self.status_code == HTTP_OK:
|
|
41
|
+
msg.extend(self._good_status_code_bad_regex_msg())
|
|
42
|
+
elif self.status_code is not None:
|
|
43
|
+
msg.extend(self._bad_status_code_msg())
|
|
44
|
+
else:
|
|
45
|
+
msg.append(self._exception_msg())
|
|
46
|
+
return list_separator.join(msg)
|
|
47
|
+
|
|
48
|
+
def _exception_msg(self) -> str:
|
|
49
|
+
return f"An error occurred during the network call: {self.raised_exception_name}"
|
|
50
|
+
|
|
51
|
+
def _bad_status_code_msg(self) -> list[str]:
|
|
52
|
+
return ["The server did not respond as expected.", f"Status code: {self.status_code}"]
|
|
53
|
+
|
|
54
|
+
def _good_status_code_bad_regex_msg(self) -> list[str]:
|
|
55
|
+
return [
|
|
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.",
|
|
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
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from enum import unique
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@unique
|
|
9
|
+
class PermissionValue(Enum):
|
|
10
|
+
RV = 1
|
|
11
|
+
V = 2
|
|
12
|
+
M = 4
|
|
13
|
+
D = 8
|
|
14
|
+
CR = 16
|
|
15
|
+
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
tmp = {
|
|
18
|
+
1: "RV",
|
|
19
|
+
2: "V",
|
|
20
|
+
4: "M",
|
|
21
|
+
8: "D",
|
|
22
|
+
16: "CR",
|
|
23
|
+
}
|
|
24
|
+
return tmp[self.value]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Permissions:
|
|
28
|
+
_permissions: dict[PermissionValue, list[str]]
|
|
29
|
+
|
|
30
|
+
def __init__(self, permissions: Optional[dict[PermissionValue, list[str]]] = None):
|
|
31
|
+
self._permissions = permissions or {}
|
|
32
|
+
|
|
33
|
+
def __str__(self) -> str:
|
|
34
|
+
tmpstr = ""
|
|
35
|
+
for permission, groups in self._permissions.items():
|
|
36
|
+
if tmpstr:
|
|
37
|
+
tmpstr += "|"
|
|
38
|
+
tmpstr += f"{permission!s} " + ",".join(groups)
|
|
39
|
+
return tmpstr
|
|
40
|
+
|
|
41
|
+
def add(self, key: PermissionValue, val: str) -> None:
|
|
42
|
+
if self._permissions.get(key) is None:
|
|
43
|
+
self._permissions[key] = [val]
|
|
44
|
+
else:
|
|
45
|
+
self._permissions[key].append(val)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from lxml import etree
|
|
4
|
+
|
|
5
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
6
|
+
from dsp_tools.commands.xmlupload.models.permission import PermissionValue
|
|
7
|
+
from dsp_tools.error.exceptions import XmlUploadError
|
|
8
|
+
from dsp_tools.legacy_models.projectContext import ProjectContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class XmlPermission:
|
|
12
|
+
"""Represents the permission set containing several XmlAllow elements in the XML used for data import"""
|
|
13
|
+
|
|
14
|
+
permission_id: str
|
|
15
|
+
_allows: list[XmlAllow]
|
|
16
|
+
|
|
17
|
+
def __init__(self, node: etree._Element, project_context: ProjectContext) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Constructor which parses a XML DOM permissions element representing an named permission set
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
node: The DOM node to be processed (representing an a permission set)
|
|
23
|
+
project_context: Context for DOM node traversal
|
|
24
|
+
"""
|
|
25
|
+
self._allows = []
|
|
26
|
+
self.permission_id = node.attrib["id"]
|
|
27
|
+
self._allows.extend(XmlAllow(allow_node, project_context) for allow_node in node)
|
|
28
|
+
|
|
29
|
+
def get_permission_instance(self) -> Permissions:
|
|
30
|
+
"""Returns a list of allow elements of this permission instance"""
|
|
31
|
+
permissions = Permissions()
|
|
32
|
+
for allow in self._allows:
|
|
33
|
+
permissions.add(allow.permission, allow.group)
|
|
34
|
+
return permissions
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
allow_str: list[str] = [f"{allow.permission} {allow.group}" for allow in self._allows]
|
|
38
|
+
return "|".join(allow_str)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class XmlAllow:
|
|
42
|
+
"""Represents the allow element of the XML used for data import"""
|
|
43
|
+
|
|
44
|
+
_group: str
|
|
45
|
+
_permission: PermissionValue
|
|
46
|
+
|
|
47
|
+
def __init__(self, node: etree._Element, project_context: ProjectContext) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Constructor which parses the XML DOM allow element
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
node: The DOM node to be processed (represents a single right in a permission set)
|
|
53
|
+
project_context: Context for DOM node traversal
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
XmlUploadError: If an upload fails
|
|
57
|
+
"""
|
|
58
|
+
tmp = node.attrib["group"].split(":")
|
|
59
|
+
|
|
60
|
+
sysgroups = ["UnknownUser", "KnownUser", "ProjectMember", "Creator", "ProjectAdmin", "SystemAdmin"]
|
|
61
|
+
if len(tmp) > 1:
|
|
62
|
+
if tmp[0]:
|
|
63
|
+
if tmp[0] == "knora-admin" and tmp[1] in sysgroups:
|
|
64
|
+
self._group = node.attrib["group"]
|
|
65
|
+
else:
|
|
66
|
+
_group = project_context.group_map.get(node.attrib["group"])
|
|
67
|
+
if _group is None:
|
|
68
|
+
raise XmlUploadError(f'Group "{node.attrib["group"]}" is not known: Cannot find project!')
|
|
69
|
+
self._group = _group
|
|
70
|
+
elif project_context.project_name is None:
|
|
71
|
+
raise XmlUploadError("Project shortname has not been set in ProjectContext")
|
|
72
|
+
else:
|
|
73
|
+
prefixed_custom_groupname = f"{project_context.project_name}:{tmp[1]}"
|
|
74
|
+
if _group := project_context.group_map.get(prefixed_custom_groupname):
|
|
75
|
+
self._group = _group
|
|
76
|
+
elif tmp[0] in sysgroups:
|
|
77
|
+
self._group = "knora-admin:" + node.attrib["group"]
|
|
78
|
+
else:
|
|
79
|
+
raise XmlUploadError(f'Group "{node.attrib["group"]}" is not known: ')
|
|
80
|
+
|
|
81
|
+
if not node.text:
|
|
82
|
+
raise XmlUploadError("No permission set specified")
|
|
83
|
+
self._permission = PermissionValue[node.text]
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def group(self) -> str:
|
|
87
|
+
"""The group specified in the allow element"""
|
|
88
|
+
return self._group
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def permission(self) -> PermissionValue:
|
|
92
|
+
"""The reference to a set of permissions"""
|
|
93
|
+
return self._permission
|
|
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
|