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,27 @@
|
|
|
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.commands.xmlupload.models.processed.file_values import ProcessedFileValue
|
|
7
|
+
from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedIIIFUri
|
|
8
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedValue
|
|
9
|
+
from dsp_tools.legacy_models.datetimestamp import DateTimeStamp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ProcessedResource:
|
|
14
|
+
res_id: str
|
|
15
|
+
type_iri: str
|
|
16
|
+
label: str
|
|
17
|
+
permissions: Permissions | None
|
|
18
|
+
values: list[ProcessedValue]
|
|
19
|
+
file_value: ProcessedFileValue | None = None
|
|
20
|
+
iiif_uri: ProcessedIIIFUri | None = None
|
|
21
|
+
migration_metadata: MigrationMetadata | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class MigrationMetadata:
|
|
26
|
+
iri_str: str | None
|
|
27
|
+
creation_date: DateTimeStamp | None
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
from dsp_tools.commands.xmlupload.models.formatted_text_value import FormattedTextValue
|
|
7
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
8
|
+
from dsp_tools.utils.data_formats.date_util import Date
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class IntervalFloats:
|
|
13
|
+
start: float
|
|
14
|
+
end: float
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
type ProcessedValueTypes = Union[bool, str, float, int, FormattedTextValue, Date, IntervalFloats]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ProcessedValue:
|
|
22
|
+
value: ProcessedValueTypes
|
|
23
|
+
prop_iri: str
|
|
24
|
+
comment: str | None
|
|
25
|
+
permissions: Permissions | None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ProcessedBoolean(ProcessedValue):
|
|
30
|
+
value: bool
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class ProcessedColor(ProcessedValue):
|
|
35
|
+
value: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ProcessedDate(ProcessedValue):
|
|
40
|
+
value: Date
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class ProcessedDecimal(ProcessedValue):
|
|
45
|
+
value: float
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class ProcessedGeoname(ProcessedValue):
|
|
50
|
+
value: str
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class ProcessedGeometry(ProcessedValue):
|
|
55
|
+
value: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class ProcessedInt(ProcessedValue):
|
|
60
|
+
value: int
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ProcessedInterval(ProcessedValue):
|
|
65
|
+
value: IntervalFloats
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class ProcessedLink(ProcessedValue):
|
|
70
|
+
value: str
|
|
71
|
+
value_uuid: str
|
|
72
|
+
|
|
73
|
+
def __post_init__(self) -> None:
|
|
74
|
+
self.prop_iri = f"{self.prop_iri}Value"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class ProcessedList(ProcessedValue):
|
|
79
|
+
value: str
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class ProcessedSimpleText(ProcessedValue):
|
|
84
|
+
value: str
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class ProcessedRichtext(ProcessedValue):
|
|
89
|
+
value: FormattedTextValue
|
|
90
|
+
resource_references: set[str]
|
|
91
|
+
value_uuid: str
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class ProcessedTime(ProcessedValue):
|
|
96
|
+
value: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class ProcessedUri(ProcessedValue):
|
|
101
|
+
value: str
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from rdflib import URIRef
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class RDFPropTypeInfo:
|
|
10
|
+
knora_type: URIRef
|
|
11
|
+
knora_prop: URIRef
|
|
12
|
+
xsd_type: URIRef | None = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class AbstractFileValue:
|
|
17
|
+
value: str
|
|
18
|
+
metadata: FileValueMetadata
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class FileValueMetadata:
|
|
23
|
+
license_iri: str
|
|
24
|
+
copyright_holder: str
|
|
25
|
+
authorships: list[str]
|
|
26
|
+
permissions: str | None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from dsp_tools.clients.legal_info_client import LegalInfoClient
|
|
4
|
+
from dsp_tools.commands.xmlupload.models.ingest import AssetClient
|
|
5
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class UploadClients:
|
|
10
|
+
"""Holds all the clients needed for the upload process."""
|
|
11
|
+
|
|
12
|
+
asset_client: AssetClient
|
|
13
|
+
list_client: ListClient
|
|
14
|
+
legal_info_client: LegalInfoClient
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import field
|
|
3
|
+
|
|
4
|
+
from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
|
|
5
|
+
from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
|
|
6
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import Stash
|
|
7
|
+
from dsp_tools.commands.xmlupload.upload_config import UploadConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class UploadState:
|
|
12
|
+
"""
|
|
13
|
+
Save the state of an xmlupload, so that after an interruption, it can be resumed.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
pending_resources: list[ProcessedResource]
|
|
17
|
+
pending_stash: Stash | None
|
|
18
|
+
config: UploadConfig
|
|
19
|
+
failed_uploads: list[str] = field(default_factory=list)
|
|
20
|
+
iri_resolver: IriResolver = field(default_factory=IriResolver)
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
import regex
|
|
7
|
+
|
|
8
|
+
from dsp_tools.error.exceptions import BaseError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def convert_ark_v0_to_resource_iri(ark: str) -> str:
|
|
12
|
+
"""
|
|
13
|
+
Converts an ARK URL from salsah.org (ARK version 0) of the form ark:/72163/080c-779b9990a0c3f-6e
|
|
14
|
+
to a DSP resource IRI of the form http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q
|
|
15
|
+
|
|
16
|
+
This method is needed for the migration of projects from salsah.org to DSP.
|
|
17
|
+
Resources need to be created with an existing ARK,
|
|
18
|
+
so the IRI needs to be extracted from that ARK
|
|
19
|
+
in order for the ARK URL to be still valid after the migration.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
ark: an ARK version 0 of the form ark:/72163/080c-779b9990a0c3f-6e,
|
|
23
|
+
'72163' being the Name Assigning Authority number,
|
|
24
|
+
'080c' being the project shortcode,
|
|
25
|
+
'779b9990a0c3f' being an ID derived from the object's Salsah ID and
|
|
26
|
+
'6e' being check digits
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
BaseError: if the ARK is invalid
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Resource IRI (str) of the form http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q
|
|
33
|
+
"""
|
|
34
|
+
# create the DaSCH namespace to create version 5 UUIDs
|
|
35
|
+
generic_namespace_url = uuid.NAMESPACE_URL
|
|
36
|
+
dasch_uuid_ns = uuid.uuid5(generic_namespace_url, "https://dasch.swiss") # cace8b00-717e-50d5-bcb9-486f39d733a2
|
|
37
|
+
|
|
38
|
+
# get the salsah resource ID from the ARK and convert it to a UUID version 5 (base64 encoded)
|
|
39
|
+
if ark.count("-") != 2:
|
|
40
|
+
raise BaseError(f"Error while converting ARK '{ark}'. The ARK seems to be invalid")
|
|
41
|
+
project_id, resource_id, _ = ark.split("-")
|
|
42
|
+
_, project_id = project_id.rsplit("/", 1)
|
|
43
|
+
project_id = project_id.upper()
|
|
44
|
+
if not regex.match("^[0-9a-fA-F]{4}$", project_id):
|
|
45
|
+
raise BaseError(f"Error while converting ARK '{ark}'. Invalid project shortcode '{project_id}'")
|
|
46
|
+
if not regex.match("^[0-9A-Za-z]+$", resource_id):
|
|
47
|
+
raise BaseError(f"Error while converting ARK '{ark}'. Invalid Salsah ID '{resource_id}'")
|
|
48
|
+
|
|
49
|
+
# make a UUID v5 from the namespace created above (which is a UUID itself) and the resource ID
|
|
50
|
+
# and encode it to base64
|
|
51
|
+
dsp_uuid = base64.urlsafe_b64encode(uuid.uuid5(dasch_uuid_ns, resource_id).bytes).decode("utf-8")
|
|
52
|
+
dsp_uuid = dsp_uuid[:-2]
|
|
53
|
+
|
|
54
|
+
# use the new UUID to create the resource IRI
|
|
55
|
+
return f"http://rdfh.ch/{project_id}/{dsp_uuid}"
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from tqdm import tqdm
|
|
6
|
+
|
|
7
|
+
from dsp_tools.commands.xmlupload.models.lookup_models import XmlReferenceLookups
|
|
8
|
+
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
9
|
+
from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedFileMetadata
|
|
10
|
+
from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedFileValue
|
|
11
|
+
from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedIIIFUri
|
|
12
|
+
from dsp_tools.commands.xmlupload.models.processed.res import MigrationMetadata
|
|
13
|
+
from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
|
|
14
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedBoolean
|
|
15
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedColor
|
|
16
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedDate
|
|
17
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedDecimal
|
|
18
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedGeometry
|
|
19
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedGeoname
|
|
20
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedInt
|
|
21
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedInterval
|
|
22
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedLink
|
|
23
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedList
|
|
24
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedRichtext
|
|
25
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedSimpleText
|
|
26
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedTime
|
|
27
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedUri
|
|
28
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedValue
|
|
29
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.ark2iri import convert_ark_v0_to_resource_iri
|
|
30
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import TypeTransformerMapper
|
|
31
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import assert_is_string
|
|
32
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import assert_is_tuple
|
|
33
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_boolean
|
|
34
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_date
|
|
35
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_decimal
|
|
36
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_geometry
|
|
37
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_integer
|
|
38
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_interval
|
|
39
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_richtext
|
|
40
|
+
from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_simpletext
|
|
41
|
+
from dsp_tools.commands.xmlupload.richtext_id2iri import find_internal_ids
|
|
42
|
+
from dsp_tools.error.exceptions import XmlUploadAuthorshipsNotFoundError
|
|
43
|
+
from dsp_tools.error.exceptions import XmlUploadListNodeNotFoundError
|
|
44
|
+
from dsp_tools.error.exceptions import XmlUploadPermissionsNotFoundError
|
|
45
|
+
from dsp_tools.legacy_models.datetimestamp import DateTimeStamp
|
|
46
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import KnoraValueType
|
|
47
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedFileValue
|
|
48
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedFileValueMetadata
|
|
49
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedMigrationMetadata
|
|
50
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedResource
|
|
51
|
+
from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedValue
|
|
52
|
+
|
|
53
|
+
TYPE_TRANSFORMER_MAPPER: dict[KnoraValueType, TypeTransformerMapper] = {
|
|
54
|
+
KnoraValueType.BOOLEAN_VALUE: TypeTransformerMapper(ProcessedBoolean, transform_boolean),
|
|
55
|
+
KnoraValueType.COLOR_VALUE: TypeTransformerMapper(ProcessedColor, assert_is_string),
|
|
56
|
+
KnoraValueType.DECIMAL_VALUE: TypeTransformerMapper(ProcessedDecimal, transform_decimal),
|
|
57
|
+
KnoraValueType.DATE_VALUE: TypeTransformerMapper(ProcessedDate, transform_date),
|
|
58
|
+
KnoraValueType.GEOM_VALUE: TypeTransformerMapper(ProcessedGeometry, transform_geometry),
|
|
59
|
+
KnoraValueType.GEONAME_VALUE: TypeTransformerMapper(ProcessedGeoname, assert_is_string),
|
|
60
|
+
KnoraValueType.INT_VALUE: TypeTransformerMapper(ProcessedInt, transform_integer),
|
|
61
|
+
KnoraValueType.INTERVAL_VALUE: TypeTransformerMapper(ProcessedInterval, transform_interval),
|
|
62
|
+
KnoraValueType.TIME_VALUE: TypeTransformerMapper(ProcessedTime, assert_is_string),
|
|
63
|
+
KnoraValueType.SIMPLETEXT_VALUE: TypeTransformerMapper(ProcessedSimpleText, transform_simpletext),
|
|
64
|
+
KnoraValueType.URI_VALUE: TypeTransformerMapper(ProcessedUri, assert_is_string),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_processed_resources(
|
|
69
|
+
resources: list[ParsedResource], lookups: XmlReferenceLookups, is_on_prod_like_server: bool
|
|
70
|
+
) -> list[ProcessedResource]:
|
|
71
|
+
logger.debug("Transform ParsedResource into ProcessedResource")
|
|
72
|
+
progress_bar = tqdm(resources, desc="Preparing data for upload", dynamic_ncols=True)
|
|
73
|
+
return [_get_one_resource(res, lookups, is_on_prod_like_server) for res in progress_bar]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_one_resource(
|
|
77
|
+
resource: ParsedResource, lookups: XmlReferenceLookups, is_on_prod_like_server: bool
|
|
78
|
+
) -> ProcessedResource:
|
|
79
|
+
permissions = _resolve_permission(resource.permissions_id, lookups.permissions)
|
|
80
|
+
values = [_get_one_processed_value(val, lookups) for val in resource.values]
|
|
81
|
+
migration_metadata = None
|
|
82
|
+
file_val, iiif_uri = _resolve_file_value(resource, lookups, is_on_prod_like_server)
|
|
83
|
+
if resource.migration_metadata:
|
|
84
|
+
migration_metadata = _get_resource_migration_metadata(resource.migration_metadata)
|
|
85
|
+
return ProcessedResource(
|
|
86
|
+
res_id=resource.res_id,
|
|
87
|
+
type_iri=resource.res_type,
|
|
88
|
+
label=resource.label,
|
|
89
|
+
permissions=permissions,
|
|
90
|
+
values=values,
|
|
91
|
+
file_value=file_val,
|
|
92
|
+
iiif_uri=iiif_uri,
|
|
93
|
+
migration_metadata=migration_metadata,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _get_resource_migration_metadata(metadata: ParsedMigrationMetadata) -> MigrationMetadata:
|
|
98
|
+
res_iri = metadata.iri
|
|
99
|
+
# ARK takes precedence over the IRI,
|
|
100
|
+
# but must be transformed into an IRI as it is only for external reference and not consistent with a DB IRI
|
|
101
|
+
if metadata.ark:
|
|
102
|
+
res_iri = convert_ark_v0_to_resource_iri(metadata.ark)
|
|
103
|
+
date = DateTimeStamp(metadata.creation_date) if metadata.creation_date else None
|
|
104
|
+
return MigrationMetadata(res_iri, date)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _resolve_file_value(
|
|
108
|
+
resource: ParsedResource, lookups: XmlReferenceLookups, is_on_prod_like_server: bool
|
|
109
|
+
) -> tuple[None | ProcessedFileValue, None | ProcessedIIIFUri]:
|
|
110
|
+
file_val, iiif_uri = None, None
|
|
111
|
+
if not resource.file_value:
|
|
112
|
+
return file_val, iiif_uri
|
|
113
|
+
|
|
114
|
+
if is_on_prod_like_server:
|
|
115
|
+
metadata = _get_file_metadata(resource.file_value.metadata, lookups)
|
|
116
|
+
else:
|
|
117
|
+
metadata = _get_file_metadata_for_test_environments(resource.file_value.metadata, lookups)
|
|
118
|
+
if resource.file_value.value_type == KnoraValueType.STILL_IMAGE_IIIF:
|
|
119
|
+
iiif_uri = _get_iiif_uri_value(resource.file_value, metadata)
|
|
120
|
+
else:
|
|
121
|
+
file_val = _get_file_value(
|
|
122
|
+
val=resource.file_value, metadata=metadata, res_id=resource.res_id, res_label=resource.label
|
|
123
|
+
)
|
|
124
|
+
return file_val, iiif_uri
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _get_file_value(
|
|
128
|
+
val: ParsedFileValue, metadata: ProcessedFileMetadata, res_id: str, res_label: str
|
|
129
|
+
) -> ProcessedFileValue:
|
|
130
|
+
file_type = cast(KnoraValueType, val.value_type)
|
|
131
|
+
file_val = assert_is_string(val.value)
|
|
132
|
+
return ProcessedFileValue(
|
|
133
|
+
value=file_val,
|
|
134
|
+
file_type=file_type,
|
|
135
|
+
metadata=metadata,
|
|
136
|
+
res_id=res_id,
|
|
137
|
+
res_label=res_label,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _get_iiif_uri_value(iiif_uri: ParsedFileValue, metadata: ProcessedFileMetadata) -> ProcessedIIIFUri:
|
|
142
|
+
file_val = assert_is_string(iiif_uri.value)
|
|
143
|
+
return ProcessedIIIFUri(file_val, metadata)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _get_file_metadata(file_metadata: ParsedFileValueMetadata, lookups: XmlReferenceLookups) -> ProcessedFileMetadata:
|
|
147
|
+
license_iri = assert_is_string(file_metadata.license_iri)
|
|
148
|
+
copyright_holder = assert_is_string(file_metadata.copyright_holder)
|
|
149
|
+
auth_id = assert_is_string(file_metadata.authorship_id)
|
|
150
|
+
authorships = _resolve_authorship(auth_id, lookups.authorships)
|
|
151
|
+
permissions = _resolve_permission(file_metadata.permissions_id, lookups.permissions)
|
|
152
|
+
return ProcessedFileMetadata(
|
|
153
|
+
license_iri=license_iri,
|
|
154
|
+
copyright_holder=copyright_holder,
|
|
155
|
+
authorships=authorships,
|
|
156
|
+
permissions=permissions,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _get_file_metadata_for_test_environments(
|
|
161
|
+
metadata: ParsedFileValueMetadata, lookups: XmlReferenceLookups
|
|
162
|
+
) -> ProcessedFileMetadata:
|
|
163
|
+
lic_iri = metadata.license_iri or "http://rdfh.ch/licenses/unknown"
|
|
164
|
+
copy_right = metadata.copyright_holder if metadata.copyright_holder else "DUMMY"
|
|
165
|
+
if not metadata.authorship_id:
|
|
166
|
+
authorship = ["DUMMY"]
|
|
167
|
+
else:
|
|
168
|
+
authorship = _resolve_authorship(metadata.authorship_id, lookups.authorships)
|
|
169
|
+
permissions = _resolve_permission(metadata.permissions_id, lookups.permissions)
|
|
170
|
+
return ProcessedFileMetadata(
|
|
171
|
+
license_iri=lic_iri,
|
|
172
|
+
copyright_holder=copy_right,
|
|
173
|
+
authorships=authorship,
|
|
174
|
+
permissions=permissions,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _resolve_authorship(authorship_id: str, lookup: dict[str, list[str]]) -> list[str]:
|
|
179
|
+
if not (found := lookup.get(authorship_id)):
|
|
180
|
+
raise XmlUploadAuthorshipsNotFoundError(f"Could not find authorships for value: {authorship_id}")
|
|
181
|
+
return found
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _get_one_processed_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
|
|
185
|
+
match val.value_type:
|
|
186
|
+
case KnoraValueType.LIST_VALUE:
|
|
187
|
+
return _get_list_value(val, lookups)
|
|
188
|
+
case KnoraValueType.LINK_VALUE:
|
|
189
|
+
return _get_link_value(val, lookups)
|
|
190
|
+
case KnoraValueType.RICHTEXT_VALUE:
|
|
191
|
+
return _get_richtext_value(val, lookups)
|
|
192
|
+
case _ as val_type:
|
|
193
|
+
transformation_mapper = TYPE_TRANSFORMER_MAPPER[val_type]
|
|
194
|
+
return _get_generic_value(val=val, lookups=lookups, transformation_mapper=transformation_mapper)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _get_generic_value(
|
|
198
|
+
val: ParsedValue, lookups: XmlReferenceLookups, transformation_mapper: TypeTransformerMapper
|
|
199
|
+
) -> ProcessedValue:
|
|
200
|
+
transformed_value = transformation_mapper.val_transformer(val.value)
|
|
201
|
+
permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
|
|
202
|
+
return transformation_mapper.val_type(transformed_value, val.prop_name, val.comment, permission_val)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _get_link_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
|
|
206
|
+
transformed_value = assert_is_string(val.value)
|
|
207
|
+
permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
|
|
208
|
+
link_val: ProcessedValue = ProcessedLink(
|
|
209
|
+
value=transformed_value,
|
|
210
|
+
prop_iri=val.prop_name,
|
|
211
|
+
comment=val.comment,
|
|
212
|
+
permissions=permission_val,
|
|
213
|
+
value_uuid=str(uuid4()),
|
|
214
|
+
)
|
|
215
|
+
return link_val
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _get_list_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
|
|
219
|
+
tuple_val = assert_is_tuple(val.value)
|
|
220
|
+
if not (list_iri := lookups.listnodes.get(tuple_val)):
|
|
221
|
+
raise XmlUploadListNodeNotFoundError(f"Could not find list IRI for value: {tuple_val}")
|
|
222
|
+
permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
|
|
223
|
+
list_val: ProcessedValue = ProcessedList(
|
|
224
|
+
value=list_iri,
|
|
225
|
+
prop_iri=val.prop_name,
|
|
226
|
+
comment=val.comment,
|
|
227
|
+
permissions=permission_val,
|
|
228
|
+
)
|
|
229
|
+
return list_val
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _get_richtext_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
|
|
233
|
+
transformed_value = transform_richtext(val.value)
|
|
234
|
+
permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
|
|
235
|
+
richtext: ProcessedValue = ProcessedRichtext(
|
|
236
|
+
value=transformed_value,
|
|
237
|
+
prop_iri=val.prop_name,
|
|
238
|
+
comment=val.comment,
|
|
239
|
+
permissions=permission_val,
|
|
240
|
+
resource_references=find_internal_ids(transformed_value.xmlstr),
|
|
241
|
+
value_uuid=str(uuid4()),
|
|
242
|
+
)
|
|
243
|
+
return richtext
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _resolve_permission(permissions: str | None, permissions_lookup: dict[str, Permissions]) -> Permissions | None:
|
|
247
|
+
"""Resolve the permission into a string that can be sent to the API."""
|
|
248
|
+
if permissions:
|
|
249
|
+
if not (per := permissions_lookup.get(permissions)):
|
|
250
|
+
raise XmlUploadPermissionsNotFoundError(f"Could not find permissions for value: {permissions}")
|
|
251
|
+
return per
|
|
252
|
+
return None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from dsp_tools.commands.xmlupload.models.input_problems import IIIFUriProblem
|
|
6
|
+
from dsp_tools.utils.data_formats.uri_util import is_iiif_uri
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class IIIFUriValidator:
|
|
11
|
+
"""Client handling communication with external IIIF-servers to do a health check."""
|
|
12
|
+
|
|
13
|
+
def validate_one_uri(self, uri: str) -> IIIFUriProblem | None:
|
|
14
|
+
"""Check if the IIIF-server is reachable. If not, it returns information for error message."""
|
|
15
|
+
regex_has_passed = is_iiif_uri(uri)
|
|
16
|
+
response = self._make_network_call(uri)
|
|
17
|
+
if isinstance(response, Exception):
|
|
18
|
+
return IIIFUriProblem(
|
|
19
|
+
uri=uri,
|
|
20
|
+
regex_has_passed=regex_has_passed,
|
|
21
|
+
raised_exception_name=response.__class__.__name__,
|
|
22
|
+
)
|
|
23
|
+
match response.ok, regex_has_passed:
|
|
24
|
+
case True, True:
|
|
25
|
+
return None
|
|
26
|
+
case _:
|
|
27
|
+
return IIIFUriProblem(
|
|
28
|
+
uri=uri,
|
|
29
|
+
regex_has_passed=regex_has_passed,
|
|
30
|
+
status_code=response.status_code,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def _make_network_call(self, uri: str) -> requests.Response | Exception:
|
|
34
|
+
info_json_uri = self._make_info_json_uri(uri)
|
|
35
|
+
try:
|
|
36
|
+
return requests.get(
|
|
37
|
+
url=info_json_uri,
|
|
38
|
+
headers={"Content-Type": "application/ld+json"},
|
|
39
|
+
timeout=10,
|
|
40
|
+
)
|
|
41
|
+
except Exception as e: # noqa: BLE001 (blind-except)
|
|
42
|
+
return e
|
|
43
|
+
|
|
44
|
+
def _make_info_json_uri(self, uri: str) -> str:
|
|
45
|
+
splt = uri.split("/")
|
|
46
|
+
if len(splt) < 5:
|
|
47
|
+
info_uri = uri.rstrip("/")
|
|
48
|
+
else:
|
|
49
|
+
info_uri = "/".join(splt[:-4])
|
|
50
|
+
return f"{info_uri}/info.json"
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from dataclasses import field
|
|
6
|
+
from typing import Any
|
|
7
|
+
from typing import Protocol
|
|
8
|
+
from urllib.parse import quote_plus
|
|
9
|
+
|
|
10
|
+
from loguru import logger
|
|
11
|
+
|
|
12
|
+
from dsp_tools.clients.connection import Connection
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class ListNode:
|
|
17
|
+
"""Information about a list node."""
|
|
18
|
+
|
|
19
|
+
node_iri: str
|
|
20
|
+
node_name: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class List:
|
|
25
|
+
"""Information about a list."""
|
|
26
|
+
|
|
27
|
+
root_iri: str
|
|
28
|
+
list_name: str
|
|
29
|
+
nodes: list[ListNode]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ProjectLists:
|
|
34
|
+
"""Information about the lists of a project."""
|
|
35
|
+
|
|
36
|
+
lists: list[List]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ListClient(Protocol):
|
|
40
|
+
"""Interface (protocol) for list-related requests to the DSP-API."""
|
|
41
|
+
|
|
42
|
+
con: Connection
|
|
43
|
+
project_iri: str
|
|
44
|
+
|
|
45
|
+
def get_list_node_id_to_iri_lookup(self) -> dict[tuple[str, str], str]:
|
|
46
|
+
"""Get a lookup of list node names to IRIs."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ListClientLive:
|
|
51
|
+
"""Client handling list-related requests to the DSP-API."""
|
|
52
|
+
|
|
53
|
+
con: Connection
|
|
54
|
+
project_iri: str
|
|
55
|
+
list_info: ProjectLists | None = field(init=False, default=None)
|
|
56
|
+
|
|
57
|
+
def get_list_node_id_to_iri_lookup(self) -> dict[tuple[str, str], str]:
|
|
58
|
+
"""
|
|
59
|
+
Get a mapping of list node IDs to their respective IRIs.
|
|
60
|
+
A list node ID is structured as follows:
|
|
61
|
+
`("<list name>", "<node name>") where the list name is the node name of the root node.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The mapping of list node IDs to IRIs.
|
|
65
|
+
"""
|
|
66
|
+
if not self.list_info:
|
|
67
|
+
self.list_info = _get_list_info_from_server(self.con, self.project_iri)
|
|
68
|
+
lookup = dict(_get_node_tuples(self.list_info.lists))
|
|
69
|
+
# Enable referencing list node IRIs in the XML:
|
|
70
|
+
# add a reference of the list node IRIs to themselves (with empty list names)
|
|
71
|
+
lookup.update({("", v): v for v in lookup.values()})
|
|
72
|
+
return lookup
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _get_node_tuples(lists: list[List]) -> Iterable[tuple[tuple[str, str], str]]:
|
|
76
|
+
for lst in lists:
|
|
77
|
+
list_name = lst.list_name
|
|
78
|
+
for node in lst.nodes:
|
|
79
|
+
yield (list_name, node.node_name), node.node_iri
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _get_list_info_from_server(con: Connection, project_iri: str) -> ProjectLists:
|
|
83
|
+
logger.info(f"Retrieving lists of project {project_iri}")
|
|
84
|
+
list_iris = _get_list_iris_from_server(con, project_iri)
|
|
85
|
+
lists = [_get_list_from_server(con, list_iri) for list_iri in list_iris]
|
|
86
|
+
return ProjectLists(lists)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _get_list_iris_from_server(con: Connection, project_iri: str) -> list[str]:
|
|
90
|
+
iri = quote_plus(project_iri)
|
|
91
|
+
res = con.get(f"/admin/lists?projectIri={iri}")
|
|
92
|
+
lists: list[dict[str, Any]] = res["lists"]
|
|
93
|
+
logger.info(f"Found {len(lists)} lists for project")
|
|
94
|
+
return [lst["id"] for lst in lists]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _get_list_from_server(con: Connection, list_iri: str) -> List:
|
|
98
|
+
logger.info(f"Retrieving nodes of list {list_iri}")
|
|
99
|
+
iri = quote_plus(list_iri)
|
|
100
|
+
res = con.get(f"/admin/lists/{iri}")
|
|
101
|
+
list_object: dict[str, Any] = res["list"]
|
|
102
|
+
list_info = list_object["listinfo"]
|
|
103
|
+
children: list[dict[str, Any]] = list_object["children"]
|
|
104
|
+
root_iri = list_info["id"]
|
|
105
|
+
# if the root node does not have a name, use the label instead
|
|
106
|
+
list_name: str = list_info.get("name") or list_info["labels"][0]["value"]
|
|
107
|
+
root_node = ListNode(root_iri, list_name)
|
|
108
|
+
nodes = [root_node, *_children_to_nodes(children)]
|
|
109
|
+
return List(root_iri, list_name, nodes)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _children_to_nodes(children: list[dict[str, Any]]) -> list[ListNode]:
|
|
113
|
+
nodes = []
|
|
114
|
+
for child in children:
|
|
115
|
+
node_iri = child["id"]
|
|
116
|
+
node_name = child["name"]
|
|
117
|
+
nodes.append(ListNode(node_iri=node_iri, node_name=node_name))
|
|
118
|
+
if "children" in child:
|
|
119
|
+
nodes.extend(_children_to_nodes(child["children"]))
|
|
120
|
+
return nodes
|