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,68 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
from dsp_tools.commands.xmlupload.models.formatted_text_value import FormattedTextValue
|
|
4
|
+
from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
|
|
5
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedLink
|
|
6
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedRichtext
|
|
7
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import LinkValueStash
|
|
8
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import LinkValueStashItem
|
|
9
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import StandoffStash
|
|
10
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import StandoffStashItem
|
|
11
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import Stash
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def stash_circular_references(resources: list[ProcessedResource], stash_lookup: dict[str, list[str]]) -> Stash | None:
|
|
15
|
+
"""Stash the values that would create circular references and remove them from the Resources."""
|
|
16
|
+
stashed_link_values: list[LinkValueStashItem] = []
|
|
17
|
+
stashed_standoff_values: list[StandoffStashItem] = []
|
|
18
|
+
|
|
19
|
+
if not stash_lookup:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
for res in resources:
|
|
23
|
+
if res.res_id not in stash_lookup:
|
|
24
|
+
continue
|
|
25
|
+
links, standoff = _process_one_resource(res, stash_lookup)
|
|
26
|
+
stashed_link_values.extend(links)
|
|
27
|
+
stashed_standoff_values.extend(standoff)
|
|
28
|
+
|
|
29
|
+
standoff_stash = StandoffStash.make(stashed_standoff_values)
|
|
30
|
+
link_value_stash = LinkValueStash.make(stashed_link_values)
|
|
31
|
+
return Stash.make(standoff_stash=standoff_stash, link_value_stash=link_value_stash)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _process_one_resource(
|
|
35
|
+
resource: ProcessedResource,
|
|
36
|
+
stash_lookup: dict[str, list[str]],
|
|
37
|
+
) -> tuple[list[LinkValueStashItem], list[StandoffStashItem]]:
|
|
38
|
+
stashed_link_values: list[LinkValueStashItem] = []
|
|
39
|
+
stashed_standoff_values: list[StandoffStashItem] = []
|
|
40
|
+
|
|
41
|
+
for val in resource.values.copy():
|
|
42
|
+
if isinstance(val, ProcessedLink):
|
|
43
|
+
if val.value_uuid not in stash_lookup[resource.res_id]:
|
|
44
|
+
continue
|
|
45
|
+
stashed_link_values.append(LinkValueStashItem(resource.res_id, resource.type_iri, val))
|
|
46
|
+
resource.values.remove(val)
|
|
47
|
+
elif isinstance(val, ProcessedRichtext):
|
|
48
|
+
if val.value_uuid not in stash_lookup[resource.res_id]:
|
|
49
|
+
continue
|
|
50
|
+
# val.value is a KnoraStandoffXml text with problematic links.
|
|
51
|
+
# stash it, then replace the problematic text with a UUID
|
|
52
|
+
stashed_standoff_values.append(_stash_standoff(val, resource.res_id, resource.type_iri))
|
|
53
|
+
|
|
54
|
+
return stashed_link_values, stashed_standoff_values
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _stash_standoff(value: ProcessedRichtext, res_id: str, res_type: str) -> StandoffStashItem:
|
|
58
|
+
original_value = deepcopy(value)
|
|
59
|
+
# Replace the content with the UUID
|
|
60
|
+
value.value = FormattedTextValue(value.value_uuid)
|
|
61
|
+
# It is not necessary to add the permissions to the StandoffStashItem.
|
|
62
|
+
# Because when no new permissions are given during an update request,
|
|
63
|
+
# the permissions of the previous value are taken.
|
|
64
|
+
return StandoffStashItem(
|
|
65
|
+
res_id=res_id,
|
|
66
|
+
res_type=res_type,
|
|
67
|
+
value=original_value,
|
|
68
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from itertools import groupby
|
|
5
|
+
|
|
6
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedLink
|
|
7
|
+
from dsp_tools.commands.xmlupload.models.processed.values import ProcessedRichtext
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class StandoffStashItem:
|
|
12
|
+
res_id: str
|
|
13
|
+
res_type: str
|
|
14
|
+
value: ProcessedRichtext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class StandoffStash:
|
|
19
|
+
"""Holds information about a number of stashed XML text values, organized by resource instance."""
|
|
20
|
+
|
|
21
|
+
res_2_stash_items: dict[str, list[StandoffStashItem]]
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def make(items: list[StandoffStashItem]) -> StandoffStash | None:
|
|
25
|
+
"""
|
|
26
|
+
Factory method for StandoffStash.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
items: A list of StandoffStashItem.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
StandoffStash | None: A StandoffStash object or None, if an empty list was passed.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def _get_res_id(x: StandoffStashItem) -> str:
|
|
36
|
+
return x.res_id
|
|
37
|
+
|
|
38
|
+
if not items:
|
|
39
|
+
return None
|
|
40
|
+
items = sorted(items, key=_get_res_id)
|
|
41
|
+
grouped_objects = {k: list(vals) for k, vals in groupby(items, key=_get_res_id)}
|
|
42
|
+
return StandoffStash(grouped_objects)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class LinkValueStashItem:
|
|
47
|
+
"""Holds information about a single stashed link value."""
|
|
48
|
+
|
|
49
|
+
res_id: str
|
|
50
|
+
res_type: str
|
|
51
|
+
value: ProcessedLink
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True)
|
|
55
|
+
class LinkValueStash:
|
|
56
|
+
"""Holds information about a number of stashed link values (resptr-props), organized by resource instance."""
|
|
57
|
+
|
|
58
|
+
res_2_stash_items: dict[str, list[LinkValueStashItem]]
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def make(items: list[LinkValueStashItem]) -> LinkValueStash | None:
|
|
62
|
+
"""
|
|
63
|
+
Factory method for LinkValueStash.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
items: A list of LinkValueStashItem.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
LinkValueStash | None: A LinkValueStash object or None, if an empty list was passed.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def _get_res_id(x: LinkValueStashItem) -> str:
|
|
73
|
+
return x.res_id
|
|
74
|
+
|
|
75
|
+
if not items:
|
|
76
|
+
return None
|
|
77
|
+
items = sorted(items, key=_get_res_id)
|
|
78
|
+
grouped_objects = {k: list(vals) for k, vals in groupby(items, key=_get_res_id)}
|
|
79
|
+
return LinkValueStash(grouped_objects)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class Stash:
|
|
84
|
+
"""Holds a standoff stash and a linkvalue stash"""
|
|
85
|
+
|
|
86
|
+
standoff_stash: StandoffStash | None
|
|
87
|
+
link_value_stash: LinkValueStash | None
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def make(standoff_stash: StandoffStash | None, link_value_stash: LinkValueStash | None) -> Stash | None:
|
|
91
|
+
"""
|
|
92
|
+
Factory method for Stash.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
standoff_stash: A StandoffStash object or None.
|
|
96
|
+
link_value_stash: A LinkValueStash object or None.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Stash: A Stash object, or None if both inputs are None.
|
|
100
|
+
"""
|
|
101
|
+
if standoff_stash or link_value_stash:
|
|
102
|
+
return Stash(standoff_stash, link_value_stash)
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def is_empty(self) -> bool:
|
|
106
|
+
"""Check if there are any stashed items in this stash"""
|
|
107
|
+
standoff = not self.standoff_stash or not self.standoff_stash.res_2_stash_items
|
|
108
|
+
link = not self.link_value_stash or not self.link_value_stash.res_2_stash_items
|
|
109
|
+
return standoff and link
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import cast
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
from rdflib import RDF
|
|
8
|
+
from rdflib import BNode
|
|
9
|
+
from rdflib import Graph
|
|
10
|
+
from rdflib import URIRef
|
|
11
|
+
from tqdm import tqdm
|
|
12
|
+
|
|
13
|
+
from dsp_tools.clients.connection import Connection
|
|
14
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.jsonld_utils import serialise_jsonld_for_value
|
|
15
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.make_values import make_link_value_graph
|
|
16
|
+
from dsp_tools.commands.xmlupload.models.upload_state import UploadState
|
|
17
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import LinkValueStash
|
|
18
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import LinkValueStashItem
|
|
19
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import Stash
|
|
20
|
+
from dsp_tools.error.exceptions import BaseError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def upload_stashed_resptr_props(
|
|
24
|
+
upload_state: UploadState,
|
|
25
|
+
con: Connection,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""
|
|
28
|
+
After all resources are uploaded, the stashed resptr props must be applied to their resources in DSP.
|
|
29
|
+
The upload state is updated accordingly, as a side effect.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
upload_state: the current state of the upload
|
|
33
|
+
con: connection to DSP
|
|
34
|
+
"""
|
|
35
|
+
logger.info("Upload the stashed links...")
|
|
36
|
+
upload_state.pending_stash = cast(Stash, upload_state.pending_stash)
|
|
37
|
+
link_value_stash = cast(LinkValueStash, upload_state.pending_stash.link_value_stash)
|
|
38
|
+
progress_bar = tqdm(
|
|
39
|
+
link_value_stash.res_2_stash_items.copy().items(), desc="Upload the stashed links", dynamic_ncols=True
|
|
40
|
+
)
|
|
41
|
+
for res_id, stash_items in progress_bar:
|
|
42
|
+
res_iri = upload_state.iri_resolver.get(res_id)
|
|
43
|
+
if not res_iri:
|
|
44
|
+
# resource could not be uploaded to DSP, so the stash cannot be uploaded either
|
|
45
|
+
# no action necessary: this resource will remain in nonapplied_resptr_props,
|
|
46
|
+
# which will be handled by the caller
|
|
47
|
+
continue
|
|
48
|
+
logger.info(f" Upload resptrs of resource '{res_id}'...")
|
|
49
|
+
for stash_item in reversed(stash_items):
|
|
50
|
+
# reversed avoids any problems caused by removing from the list we loop over at the same time
|
|
51
|
+
target_iri = upload_state.iri_resolver.get(stash_item.value.value)
|
|
52
|
+
if not target_iri:
|
|
53
|
+
continue
|
|
54
|
+
if _upload_stash_item(stash_item, res_iri, target_iri, con):
|
|
55
|
+
link_value_stash.res_2_stash_items[res_id].remove(stash_item)
|
|
56
|
+
# remove res_id if all stash items were uploaded
|
|
57
|
+
if not link_value_stash.res_2_stash_items[res_id]:
|
|
58
|
+
del link_value_stash.res_2_stash_items[res_id]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _upload_stash_item(
|
|
62
|
+
stash: LinkValueStashItem,
|
|
63
|
+
res_iri: str,
|
|
64
|
+
target_iri: str,
|
|
65
|
+
con: Connection,
|
|
66
|
+
) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
Upload a single stashed link value to DSP.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
stash: the stashed link value to upload
|
|
72
|
+
res_iri: the iri of the resource
|
|
73
|
+
target_iri: the iri of the target resource
|
|
74
|
+
con: connection to DSP
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True, if the upload was successful, False otherwise
|
|
78
|
+
"""
|
|
79
|
+
graph = _make_link_value_create_graph(stash, res_iri, target_iri)
|
|
80
|
+
payload = serialise_jsonld_for_value(graph, res_iri)
|
|
81
|
+
try:
|
|
82
|
+
con.post(route="/v2/values", data=payload)
|
|
83
|
+
except BaseError as err:
|
|
84
|
+
_log_unable_to_upload_link_value(err.message, stash.res_id, stash.value.prop_iri)
|
|
85
|
+
return False
|
|
86
|
+
logger.debug(f' Successfully uploaded resptr links of "{stash.value.prop_iri}"')
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _make_link_value_create_graph(
|
|
91
|
+
stash: LinkValueStashItem,
|
|
92
|
+
res_iri_str: str,
|
|
93
|
+
target_iri: str,
|
|
94
|
+
) -> Graph:
|
|
95
|
+
"""This function creates a JSON object that can be sent as an update request to the DSP-API."""
|
|
96
|
+
val_bn = BNode()
|
|
97
|
+
res_iri = URIRef(res_iri_str)
|
|
98
|
+
graph = make_link_value_graph(stash.value, val_bn, res_iri, URIRef(target_iri))
|
|
99
|
+
graph.add((res_iri, RDF.type, URIRef(stash.res_type)))
|
|
100
|
+
return graph
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _log_unable_to_upload_link_value(msg: str, res_id: str, prop_name: str) -> None:
|
|
104
|
+
err_msg = f"Unable to upload the resptr prop of '{prop_name}' of resource '{res_id}'."
|
|
105
|
+
print(f"{datetime.now()}: WARNING: {err_msg} Original error message: {msg}")
|
|
106
|
+
logger.error(err_msg)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
from typing import cast
|
|
6
|
+
from urllib.parse import quote_plus
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
from rdflib import RDF
|
|
10
|
+
from rdflib import Graph
|
|
11
|
+
from rdflib import URIRef
|
|
12
|
+
from tqdm import tqdm
|
|
13
|
+
|
|
14
|
+
from dsp_tools.clients.connection import Connection
|
|
15
|
+
from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
|
|
16
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.jsonld_utils import serialise_jsonld_for_value
|
|
17
|
+
from dsp_tools.commands.xmlupload.make_rdf_graph.make_values import make_richtext_value_graph
|
|
18
|
+
from dsp_tools.commands.xmlupload.models.upload_state import UploadState
|
|
19
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import StandoffStash
|
|
20
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import StandoffStashItem
|
|
21
|
+
from dsp_tools.commands.xmlupload.stash.stash_models import Stash
|
|
22
|
+
from dsp_tools.error.exceptions import BaseError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def upload_stashed_xml_texts(upload_state: UploadState, con: Connection) -> None:
|
|
26
|
+
"""
|
|
27
|
+
After all resources are uploaded, the stashed xml texts must be applied to their resources in DSP.
|
|
28
|
+
The upload state is updated accordingly, as a side effect.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
upload_state: the current state of the upload
|
|
32
|
+
con: connection to DSP
|
|
33
|
+
"""
|
|
34
|
+
logger.info("Upload the stashed XML texts...")
|
|
35
|
+
upload_state.pending_stash = cast(Stash, upload_state.pending_stash)
|
|
36
|
+
standoff_stash = cast(StandoffStash, upload_state.pending_stash.standoff_stash)
|
|
37
|
+
progress_bar = tqdm(
|
|
38
|
+
standoff_stash.res_2_stash_items.copy().items(), desc="Upload stashed XML texts", dynamic_ncols=True
|
|
39
|
+
)
|
|
40
|
+
for res_id, stash_items in progress_bar:
|
|
41
|
+
res_iri = upload_state.iri_resolver.get(res_id)
|
|
42
|
+
if not res_iri:
|
|
43
|
+
# resource could not be uploaded to DSP, so the stash cannot be uploaded either
|
|
44
|
+
# no action necessary: this resource will remain in the list of not uploaded stash items,
|
|
45
|
+
# which will be handled by the caller
|
|
46
|
+
continue
|
|
47
|
+
try:
|
|
48
|
+
resource_in_triplestore = con.get(f"/v2/resources/{quote_plus(res_iri)}")
|
|
49
|
+
except BaseError as err:
|
|
50
|
+
_log_unable_to_retrieve_resource(resource=res_id, received_error=err)
|
|
51
|
+
continue
|
|
52
|
+
logger.info(f" Upload XML text(s) of resource '{res_id}'...")
|
|
53
|
+
for stash_item in stash_items:
|
|
54
|
+
value_iri = _get_value_iri(stash_item.value.prop_iri, resource_in_triplestore, stash_item.value.value_uuid)
|
|
55
|
+
if not value_iri:
|
|
56
|
+
continue
|
|
57
|
+
if _upload_stash_item(
|
|
58
|
+
stash_item=stash_item,
|
|
59
|
+
res_iri=res_iri,
|
|
60
|
+
value_iri=value_iri,
|
|
61
|
+
iri_resolver=upload_state.iri_resolver,
|
|
62
|
+
con=con,
|
|
63
|
+
):
|
|
64
|
+
standoff_stash.res_2_stash_items[res_id].remove(stash_item)
|
|
65
|
+
if not standoff_stash.res_2_stash_items[res_id]:
|
|
66
|
+
standoff_stash.res_2_stash_items.pop(res_id)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _get_value_iri(
|
|
70
|
+
property_name: str,
|
|
71
|
+
resource: dict[str, Any],
|
|
72
|
+
uuid: str,
|
|
73
|
+
) -> str | None:
|
|
74
|
+
prefixed_prop = _make_prefixed_prop_from_absolute_iri(property_name)
|
|
75
|
+
values_on_server = resource.get(prefixed_prop)
|
|
76
|
+
if not isinstance(values_on_server, list):
|
|
77
|
+
values_on_server = [values_on_server]
|
|
78
|
+
|
|
79
|
+
# get the IRI of the value that contains the UUID in its text
|
|
80
|
+
text_and_iris = ((v["knora-api:textValueAsXml"], v["@id"]) for v in values_on_server)
|
|
81
|
+
value_iri: str | None = next((iri for text, iri in text_and_iris if uuid in text), None)
|
|
82
|
+
# in case that "value_iri" is None, the value that contains the UUID in its text does not exist in DSP
|
|
83
|
+
# no action necessary: this resource will remain in nonapplied_xml_texts,
|
|
84
|
+
# which will be handled by the caller
|
|
85
|
+
return value_iri
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _make_prefixed_prop_from_absolute_iri(absolute_iri: str) -> str:
|
|
89
|
+
_, onto, prop = absolute_iri.rsplit("/", 2)
|
|
90
|
+
local_name = prop.split("#")[-1]
|
|
91
|
+
return f"{onto}:{local_name}"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _upload_stash_item(
|
|
95
|
+
stash_item: StandoffStashItem,
|
|
96
|
+
res_iri: str,
|
|
97
|
+
value_iri: str,
|
|
98
|
+
iri_resolver: IriResolver,
|
|
99
|
+
con: Connection,
|
|
100
|
+
) -> bool:
|
|
101
|
+
"""
|
|
102
|
+
Upload a single stashed xml text to DSP.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
stash_item: the stashed text value to upload
|
|
106
|
+
res_iri: the iri of the resource
|
|
107
|
+
value_iri: the iri of the value
|
|
108
|
+
iri_resolver: resolver to map ids from the XML file to IRIs in DSP
|
|
109
|
+
con: connection to DSP
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True, if the upload was successful, False otherwise
|
|
113
|
+
"""
|
|
114
|
+
payload = _serialise_richtext_for_update(
|
|
115
|
+
stash_item=stash_item,
|
|
116
|
+
value_iri_str=value_iri,
|
|
117
|
+
res_iri_str=res_iri,
|
|
118
|
+
iri_resolver=iri_resolver,
|
|
119
|
+
)
|
|
120
|
+
try:
|
|
121
|
+
con.put(route="/v2/values", data=payload)
|
|
122
|
+
except BaseError as err:
|
|
123
|
+
_log_unable_to_upload_xml_resource(err, stash_item.res_id, stash_item.value.prop_iri)
|
|
124
|
+
return False
|
|
125
|
+
logger.debug(f' Successfully uploaded xml text of "{stash_item.value.prop_iri}"')
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _serialise_richtext_for_update(
|
|
130
|
+
stash_item: StandoffStashItem, value_iri_str: str, res_iri_str: str, iri_resolver: IriResolver
|
|
131
|
+
) -> dict[str, Any]:
|
|
132
|
+
graph = _make_richtext_update_graph(
|
|
133
|
+
stash_item=stash_item,
|
|
134
|
+
value_iri_str=value_iri_str,
|
|
135
|
+
res_iri_str=res_iri_str,
|
|
136
|
+
iri_resolver=iri_resolver,
|
|
137
|
+
)
|
|
138
|
+
return serialise_jsonld_for_value(graph, res_iri_str)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _make_richtext_update_graph(
|
|
142
|
+
stash_item: StandoffStashItem, value_iri_str: str, res_iri_str: str, iri_resolver: IriResolver
|
|
143
|
+
) -> Graph:
|
|
144
|
+
res_iri = URIRef(res_iri_str)
|
|
145
|
+
value_iri = URIRef(value_iri_str)
|
|
146
|
+
val_graph = make_richtext_value_graph(
|
|
147
|
+
val=stash_item.value,
|
|
148
|
+
val_node=value_iri,
|
|
149
|
+
res_node=res_iri,
|
|
150
|
+
iri_resolver=iri_resolver,
|
|
151
|
+
)
|
|
152
|
+
val_graph.add((res_iri, RDF.type, URIRef(stash_item.res_type)))
|
|
153
|
+
return val_graph
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _log_unable_to_retrieve_resource(
|
|
157
|
+
resource: str,
|
|
158
|
+
received_error: BaseError,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""
|
|
161
|
+
This function logs the error if it is not possible to retrieve the resource.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
resource: the resource id
|
|
165
|
+
received_error: the error
|
|
166
|
+
"""
|
|
167
|
+
# print the message to keep track of the cause for the failure
|
|
168
|
+
# apart from that; no action is necessary:
|
|
169
|
+
# this resource will remain in nonapplied_xml_texts, which will be handled by the caller
|
|
170
|
+
err_msg = (
|
|
171
|
+
f"Unable to upload XML texts of resource '{resource}', "
|
|
172
|
+
"because the resource cannot be retrieved from the DSP server."
|
|
173
|
+
)
|
|
174
|
+
print(f"{datetime.now()}: WARNING: {err_msg} Original error message: {received_error.message}")
|
|
175
|
+
logger.error(err_msg)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _log_unable_to_upload_xml_resource(
|
|
179
|
+
received_error: BaseError,
|
|
180
|
+
stashed_resource_id: str,
|
|
181
|
+
prop_name: str,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""
|
|
184
|
+
This function logs if it is not possible to upload a xml resource.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
received_error: Error received
|
|
188
|
+
stashed_resource_id: id of the resource
|
|
189
|
+
prop_name: name of the property
|
|
190
|
+
"""
|
|
191
|
+
# print the message to keep track of the cause for the failure
|
|
192
|
+
# apart from that; no action is necessary:
|
|
193
|
+
# this resource will remain in nonapplied_xml_texts, which will be handled by the caller
|
|
194
|
+
err_msg = f"Unable to upload the xml text of '{prop_name}' of resource '{stashed_resource_id}'."
|
|
195
|
+
print(f"{datetime.now()}: WARNING: {err_msg} Original error message: {received_error.message}")
|
|
196
|
+
logger.error(err_msg)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from dataclasses import field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import regex
|
|
9
|
+
from loguru import logger
|
|
10
|
+
|
|
11
|
+
from dsp_tools.cli.args import ValidationSeverity
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _transform_server_url_to_foldername(server: str) -> str:
|
|
15
|
+
"""
|
|
16
|
+
Take the server URL and transform it so that it can be used as foldername.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
server: server, e.g. "https://api.test.dasch.swiss/" or "http://0.0.0.0:3333"
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
simplified version, e.g. "test.dasch.swiss" or "localhost"
|
|
23
|
+
"""
|
|
24
|
+
server_substitutions = {
|
|
25
|
+
r"https?://": "",
|
|
26
|
+
r"^api\.": "",
|
|
27
|
+
r":\d{2,5}/?$": "",
|
|
28
|
+
r"/$": "",
|
|
29
|
+
r"0.0.0.0": "localhost", # noqa: S104 (hardcoded-bind-all-interfaces)
|
|
30
|
+
}
|
|
31
|
+
for pattern, repl in server_substitutions.items():
|
|
32
|
+
server = regex.sub(pattern, repl, server)
|
|
33
|
+
return server
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class DiagnosticsConfig:
|
|
38
|
+
"""Configures all diagnostics for a given upload."""
|
|
39
|
+
|
|
40
|
+
server_as_foldername: str = "unknown"
|
|
41
|
+
save_location: Path = field(default=Path.home() / ".dsp-tools" / "xmluploads")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class UploadConfig:
|
|
46
|
+
"""Configuration for the upload process."""
|
|
47
|
+
|
|
48
|
+
media_previously_uploaded: bool = False
|
|
49
|
+
server: str = "unknown"
|
|
50
|
+
shortcode: str = "unknown"
|
|
51
|
+
diagnostics: DiagnosticsConfig = field(default_factory=DiagnosticsConfig)
|
|
52
|
+
interrupt_after: int | None = None
|
|
53
|
+
skip_iiif_validation: bool = False
|
|
54
|
+
skip_validation: bool = False
|
|
55
|
+
skip_ontology_validation: bool = False
|
|
56
|
+
ignore_duplicate_files_warning: bool = False
|
|
57
|
+
validation_severity: ValidationSeverity = field(default_factory=lambda: ValidationSeverity.INFO)
|
|
58
|
+
id2iri_file: str | None = None
|
|
59
|
+
do_not_request_resource_metadata_from_db: bool = False
|
|
60
|
+
|
|
61
|
+
def with_server_info(
|
|
62
|
+
self,
|
|
63
|
+
server: str,
|
|
64
|
+
shortcode: str,
|
|
65
|
+
) -> UploadConfig:
|
|
66
|
+
"""Create a new UploadConfig with the given server."""
|
|
67
|
+
server_as_foldername = _transform_server_url_to_foldername(server)
|
|
68
|
+
save_location = Path.home() / Path(".dsp-tools") / "xmluploads" / server_as_foldername / "resumable/latest.pkl"
|
|
69
|
+
save_location.parent.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
logger.info(f"{save_location=:}")
|
|
71
|
+
diagnostics: DiagnosticsConfig = dataclasses.replace(
|
|
72
|
+
self.diagnostics,
|
|
73
|
+
server_as_foldername=server_as_foldername,
|
|
74
|
+
save_location=save_location,
|
|
75
|
+
)
|
|
76
|
+
return dataclasses.replace(self, server=server, shortcode=shortcode, diagnostics=diagnostics)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from dsp_tools.commands.xmlupload.upload_config import DiagnosticsConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def write_id2iri_mapping(id2iri_mapping: dict[str, str], shortcode: str, diagnostics: DiagnosticsConfig) -> None:
|
|
13
|
+
"""Writes the mapping of internal IDs to IRIs to a file."""
|
|
14
|
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
|
15
|
+
servername = diagnostics.server_as_foldername
|
|
16
|
+
json_str = json.dumps(id2iri_mapping, ensure_ascii=False, indent=4)
|
|
17
|
+
id2iri_filename_for_user = f"id2iri_{shortcode}_{servername}_{timestamp}.json"
|
|
18
|
+
with open(id2iri_filename_for_user, "w", encoding="utf-8") as f:
|
|
19
|
+
f.write(json_str)
|
|
20
|
+
print(f"{datetime.now()}: The mapping of internal IDs to IRIs was written to {id2iri_filename_for_user}")
|
|
21
|
+
logger.info(f"The mapping of internal IDs to IRIs was written to {id2iri_filename_for_user}")
|
|
22
|
+
|
|
23
|
+
id_2_iri_folder = Path.home() / ".dsp-tools" / "id2iri"
|
|
24
|
+
id_2_iri_folder.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
id2iri_filename_for_user_home = f"id2iri_{timestamp}_{shortcode}_{servername}.json"
|
|
26
|
+
with open(id_2_iri_folder / id2iri_filename_for_user_home, "w", encoding="utf-8") as f:
|
|
27
|
+
f.write(json_str)
|