dsp-tools 9.1.0.post11__py3-none-any.whl → 18.3.0.post13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsp_tools/__init__.py +4 -0
- dsp_tools/cli/args.py +36 -0
- dsp_tools/cli/call_action.py +51 -231
- dsp_tools/cli/call_action_files_only.py +101 -0
- dsp_tools/cli/call_action_with_network.py +207 -0
- dsp_tools/cli/create_parsers.py +156 -58
- dsp_tools/cli/entry_point.py +56 -26
- dsp_tools/cli/utils.py +87 -0
- dsp_tools/clients/CLAUDE.md +420 -0
- dsp_tools/clients/authentication_client.py +14 -0
- dsp_tools/clients/authentication_client_live.py +66 -0
- dsp_tools/{utils → clients}/connection.py +2 -18
- dsp_tools/clients/connection_live.py +233 -0
- dsp_tools/clients/fuseki_metrics.py +60 -0
- dsp_tools/clients/group_user_clients.py +35 -0
- dsp_tools/clients/group_user_clients_live.py +181 -0
- dsp_tools/clients/legal_info_client.py +23 -0
- dsp_tools/clients/legal_info_client_live.py +132 -0
- dsp_tools/clients/list_client.py +49 -0
- dsp_tools/clients/list_client_live.py +166 -0
- dsp_tools/clients/metadata_client.py +24 -0
- dsp_tools/clients/metadata_client_live.py +47 -0
- dsp_tools/clients/ontology_clients.py +49 -0
- dsp_tools/clients/ontology_create_client_live.py +166 -0
- dsp_tools/clients/ontology_get_client_live.py +80 -0
- dsp_tools/clients/permissions_client.py +68 -0
- dsp_tools/clients/project_client.py +16 -0
- dsp_tools/clients/project_client_live.py +66 -0
- dsp_tools/commands/create/communicate_problems.py +24 -0
- dsp_tools/commands/create/create.py +134 -0
- dsp_tools/commands/create/create_on_server/cardinalities.py +111 -0
- dsp_tools/commands/create/create_on_server/classes.py +99 -0
- dsp_tools/commands/create/create_on_server/complete_ontologies.py +116 -0
- dsp_tools/commands/create/create_on_server/default_permissions.py +134 -0
- dsp_tools/commands/create/create_on_server/group_users.py +165 -0
- dsp_tools/commands/create/create_on_server/lists.py +163 -0
- dsp_tools/commands/create/create_on_server/mappers.py +12 -0
- dsp_tools/commands/create/create_on_server/onto_utils.py +74 -0
- dsp_tools/commands/create/create_on_server/ontology.py +52 -0
- dsp_tools/commands/create/create_on_server/project.py +68 -0
- dsp_tools/commands/create/create_on_server/properties.py +119 -0
- dsp_tools/commands/create/exceptions.py +29 -0
- dsp_tools/commands/create/lists_only.py +66 -0
- dsp_tools/commands/create/models/create_problems.py +87 -0
- dsp_tools/commands/create/models/parsed_ontology.py +88 -0
- dsp_tools/commands/create/models/parsed_project.py +81 -0
- dsp_tools/commands/create/models/rdf_ontology.py +12 -0
- dsp_tools/commands/create/models/server_project_info.py +100 -0
- dsp_tools/commands/create/parsing/parse_lists.py +45 -0
- dsp_tools/commands/create/parsing/parse_ontology.py +243 -0
- dsp_tools/commands/create/parsing/parse_project.py +149 -0
- dsp_tools/commands/create/parsing/parsing_utils.py +40 -0
- dsp_tools/commands/create/project_validate.py +595 -0
- dsp_tools/commands/create/serialisation/ontology.py +119 -0
- dsp_tools/commands/create/serialisation/project.py +44 -0
- dsp_tools/commands/excel2json/CLAUDE.md +101 -0
- dsp_tools/commands/excel2json/json_header.py +57 -23
- dsp_tools/commands/excel2json/{new_lists → lists}/compliance_checks.py +26 -26
- dsp_tools/commands/excel2json/{new_lists/make_new_lists.py → lists/make_lists.py} +19 -18
- dsp_tools/commands/excel2json/{new_lists → lists}/models/input_error.py +1 -12
- dsp_tools/commands/excel2json/{new_lists → lists}/models/serialise.py +9 -5
- dsp_tools/commands/excel2json/{new_lists → lists}/utils.py +4 -4
- dsp_tools/commands/excel2json/models/input_error.py +31 -11
- dsp_tools/commands/excel2json/models/json_header.py +53 -15
- dsp_tools/commands/excel2json/models/ontology.py +4 -3
- dsp_tools/commands/excel2json/{lists.py → old_lists.py} +26 -112
- dsp_tools/commands/excel2json/project.py +78 -34
- dsp_tools/commands/excel2json/properties.py +57 -36
- dsp_tools/commands/excel2json/resources.py +32 -12
- dsp_tools/commands/excel2json/utils.py +20 -1
- dsp_tools/commands/excel2xml/__init__.py +2 -2
- dsp_tools/commands/excel2xml/excel2xml_cli.py +7 -15
- dsp_tools/commands/excel2xml/excel2xml_lib.py +138 -493
- dsp_tools/commands/excel2xml/propertyelement.py +5 -5
- dsp_tools/commands/{project → get}/get.py +29 -13
- dsp_tools/commands/get/get_permissions.py +257 -0
- dsp_tools/commands/get/get_permissions_legacy.py +89 -0
- dsp_tools/commands/{project/models → get/legacy_models}/context.py +6 -6
- dsp_tools/commands/{project/models → get/legacy_models}/group.py +5 -10
- dsp_tools/commands/{project/models → get/legacy_models}/listnode.py +5 -35
- dsp_tools/commands/{project/models → get/legacy_models}/model.py +1 -1
- dsp_tools/commands/{project/models → get/legacy_models}/ontology.py +9 -14
- dsp_tools/commands/{project/models → get/legacy_models}/project.py +13 -6
- dsp_tools/commands/{project/models → get/legacy_models}/propertyclass.py +9 -16
- dsp_tools/commands/{project/models → get/legacy_models}/resourceclass.py +8 -46
- dsp_tools/commands/{project/models → get/legacy_models}/user.py +19 -60
- dsp_tools/commands/get/models/permissions_models.py +10 -0
- dsp_tools/commands/id2iri.py +20 -10
- dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +81 -56
- dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +4 -10
- dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +97 -37
- dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +2 -2
- dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +9 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +3 -3
- dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +2 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +12 -2
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +8 -9
- dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +18 -18
- dsp_tools/commands/start_stack.py +126 -77
- dsp_tools/commands/update_legal/CLAUDE.md +344 -0
- dsp_tools/commands/update_legal/__init__.py +0 -0
- dsp_tools/commands/update_legal/core.py +182 -0
- dsp_tools/commands/update_legal/csv_operations.py +135 -0
- dsp_tools/commands/update_legal/models.py +87 -0
- dsp_tools/commands/update_legal/xml_operations.py +247 -0
- dsp_tools/commands/validate_data/CLAUDE.md +159 -0
- dsp_tools/commands/validate_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/constants.py +59 -0
- dsp_tools/commands/validate_data/mappers.py +143 -0
- dsp_tools/commands/validate_data/models/__init__.py +0 -0
- dsp_tools/commands/validate_data/models/api_responses.py +45 -0
- dsp_tools/commands/validate_data/models/input_problems.py +119 -0
- dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
- dsp_tools/commands/validate_data/models/validation.py +106 -0
- dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
- dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
- dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
- dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
- dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
- dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
- dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
- dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
- dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
- dsp_tools/commands/{xml_validate/sparql/resource_shacl.py → validate_data/sparql/cardinality_shacl.py} +45 -47
- dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
- dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
- dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
- dsp_tools/commands/validate_data/utils.py +59 -0
- dsp_tools/commands/validate_data/validate_data.py +283 -0
- dsp_tools/commands/validate_data/validation/__init__.py +0 -0
- dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
- dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
- dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
- dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
- dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
- dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
- dsp_tools/commands/xmlupload/models/formatted_text_value.py +0 -25
- dsp_tools/commands/xmlupload/models/ingest.py +56 -70
- dsp_tools/commands/xmlupload/models/input_problems.py +6 -14
- dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
- dsp_tools/commands/xmlupload/models/permission.py +0 -39
- dsp_tools/commands/xmlupload/models/{deserialise/xmlpermission.py → permissions_parsed.py} +2 -2
- dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
- dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
- dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
- dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
- dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
- dsp_tools/commands/xmlupload/models/upload_clients.py +3 -3
- dsp_tools/commands/xmlupload/models/upload_state.py +2 -4
- dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
- dsp_tools/commands/xmlupload/{ark2iri.py → prepare_xml_input/ark2iri.py} +1 -1
- dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
- dsp_tools/commands/xmlupload/{iiif_uri_validator.py → prepare_xml_input/iiif_uri_validator.py} +2 -14
- dsp_tools/commands/xmlupload/{list_client.py → prepare_xml_input/list_client.py} +15 -10
- dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
- dsp_tools/commands/xmlupload/resource_create_client.py +7 -468
- dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
- dsp_tools/commands/xmlupload/stash/{construct_and_analyze_graph.py → analyse_circular_reference_graph.py} +64 -157
- dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
- dsp_tools/commands/xmlupload/stash/graph_models.py +13 -8
- dsp_tools/commands/xmlupload/stash/stash_circular_references.py +48 -115
- dsp_tools/commands/xmlupload/stash/stash_models.py +4 -9
- dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +34 -40
- dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +98 -108
- dsp_tools/commands/xmlupload/upload_config.py +8 -0
- dsp_tools/commands/xmlupload/write_diagnostic_info.py +14 -9
- dsp_tools/commands/xmlupload/xmlupload.py +214 -192
- dsp_tools/config/__init__.py +0 -0
- dsp_tools/config/logger_config.py +69 -0
- dsp_tools/{utils → config}/warnings_config.py +4 -1
- dsp_tools/error/__init__.py +0 -0
- dsp_tools/error/custom_warnings.py +39 -0
- dsp_tools/error/exceptions.py +204 -0
- dsp_tools/error/problems.py +10 -0
- dsp_tools/error/xmllib_errors.py +20 -0
- dsp_tools/error/xmllib_warnings.py +54 -0
- dsp_tools/error/xmllib_warnings_util.py +159 -0
- dsp_tools/error/xsd_validation_error_msg.py +19 -0
- dsp_tools/legacy_models/__init__.py +0 -0
- dsp_tools/{models → legacy_models}/datetimestamp.py +7 -7
- dsp_tools/{models → legacy_models}/langstring.py +1 -1
- dsp_tools/{models → legacy_models}/projectContext.py +4 -4
- dsp_tools/resources/schema/data.xsd +108 -83
- dsp_tools/resources/schema/lists-only.json +4 -23
- dsp_tools/resources/schema/project.json +80 -35
- dsp_tools/resources/schema/properties-only.json +1 -4
- dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
- dsp_tools/resources/start-stack/docker-compose.yml +34 -30
- dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
- dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
- dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
- dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
- dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
- dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
- dsp_tools/utils/ansi_colors.py +32 -0
- dsp_tools/utils/data_formats/__init__.py +0 -0
- dsp_tools/utils/{date_util.py → data_formats/date_util.py} +13 -1
- dsp_tools/utils/data_formats/iri_util.py +30 -0
- dsp_tools/utils/{shared.py → data_formats/shared.py} +1 -35
- dsp_tools/utils/{uri_util.py → data_formats/uri_util.py} +12 -2
- dsp_tools/utils/fuseki_bloating.py +63 -0
- dsp_tools/utils/json_parsing.py +22 -0
- dsp_tools/utils/rdf_constants.py +42 -0
- dsp_tools/utils/rdflib_utils.py +10 -0
- dsp_tools/utils/replace_id_with_iri.py +66 -0
- dsp_tools/utils/request_utils.py +238 -0
- dsp_tools/utils/xml_parsing/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
- dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
- dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
- dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
- dsp_tools/xmllib/CLAUDE.md +302 -0
- dsp_tools/xmllib/__init__.py +49 -0
- dsp_tools/xmllib/general_functions.py +877 -0
- dsp_tools/xmllib/internal/__init__.py +0 -0
- dsp_tools/xmllib/internal/checkers.py +162 -0
- dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
- dsp_tools/xmllib/internal/constants.py +46 -0
- dsp_tools/xmllib/internal/input_converters.py +155 -0
- dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
- dsp_tools/xmllib/internal/serialise_resource.py +177 -0
- dsp_tools/xmllib/internal/serialise_values.py +152 -0
- dsp_tools/xmllib/internal/type_aliases.py +11 -0
- dsp_tools/xmllib/models/config_options.py +28 -0
- dsp_tools/xmllib/models/date_formats.py +48 -0
- dsp_tools/xmllib/models/dsp_base_resources.py +1380 -400
- dsp_tools/xmllib/models/internal/__init__.py +0 -0
- dsp_tools/xmllib/models/internal/file_values.py +172 -0
- dsp_tools/xmllib/models/internal/geometry.py +162 -0
- dsp_tools/xmllib/models/{migration_metadata.py → internal/migration_metadata.py} +14 -10
- dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
- dsp_tools/xmllib/models/internal/values.py +342 -0
- dsp_tools/xmllib/models/licenses/__init__.py +0 -0
- dsp_tools/xmllib/models/licenses/other.py +59 -0
- dsp_tools/xmllib/models/licenses/recommended.py +107 -0
- dsp_tools/xmllib/models/permissions.py +41 -0
- dsp_tools/xmllib/models/res.py +1782 -0
- dsp_tools/xmllib/models/root.py +313 -26
- dsp_tools/xmllib/value_checkers.py +310 -47
- dsp_tools/xmllib/value_converters.py +765 -8
- dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
- dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
- dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
- {dsp_tools-9.1.0.post11.dist-info → dsp_tools-18.3.0.post13.dist-info}/entry_points.txt +1 -0
- dsp_tools/commands/project/create/project_create.py +0 -1107
- dsp_tools/commands/project/create/project_create_lists.py +0 -204
- dsp_tools/commands/project/create/project_validate.py +0 -453
- dsp_tools/commands/project/models/project_definition.py +0 -12
- dsp_tools/commands/rosetta.py +0 -124
- dsp_tools/commands/template.py +0 -30
- dsp_tools/commands/xml_validate/api_connection.py +0 -122
- dsp_tools/commands/xml_validate/deserialise_input.py +0 -135
- dsp_tools/commands/xml_validate/make_data_rdf.py +0 -193
- dsp_tools/commands/xml_validate/models/data_deserialised.py +0 -108
- dsp_tools/commands/xml_validate/models/data_rdf.py +0 -214
- dsp_tools/commands/xml_validate/models/input_problems.py +0 -191
- dsp_tools/commands/xml_validate/models/validation.py +0 -29
- dsp_tools/commands/xml_validate/reformat_validaton_result.py +0 -89
- dsp_tools/commands/xml_validate/sparql/construct_shapes.py +0 -16
- dsp_tools/commands/xml_validate/xml_validate.py +0 -151
- dsp_tools/commands/xmlupload/check_consistency_with_ontology.py +0 -253
- dsp_tools/commands/xmlupload/models/deserialise/deserialise_value.py +0 -236
- dsp_tools/commands/xmlupload/models/deserialise/xmlresource.py +0 -171
- dsp_tools/commands/xmlupload/models/namespace_context.py +0 -39
- dsp_tools/commands/xmlupload/models/ontology_lookup_models.py +0 -161
- dsp_tools/commands/xmlupload/models/ontology_problem_models.py +0 -178
- dsp_tools/commands/xmlupload/models/serialise/jsonld_serialiser.py +0 -40
- dsp_tools/commands/xmlupload/models/serialise/serialise_value.py +0 -51
- dsp_tools/commands/xmlupload/ontology_client.py +0 -92
- dsp_tools/commands/xmlupload/project_client.py +0 -91
- dsp_tools/commands/xmlupload/read_validate_xml_file.py +0 -99
- dsp_tools/models/custom_warnings.py +0 -31
- dsp_tools/models/exceptions.py +0 -90
- dsp_tools/resources/0100-template-repo/template.json +0 -45
- dsp_tools/resources/0100-template-repo/template.xml +0 -27
- dsp_tools/resources/start-stack/docker-compose-validation.yml +0 -5
- dsp_tools/resources/start-stack/start-stack-config.yml +0 -4
- dsp_tools/resources/xml_validate/api-shapes.ttl +0 -411
- dsp_tools/resources/xml_validate/replace_namespace.xslt +0 -61
- dsp_tools/utils/connection_live.py +0 -383
- dsp_tools/utils/iri_util.py +0 -14
- dsp_tools/utils/logger_config.py +0 -41
- dsp_tools/utils/set_encoder.py +0 -20
- dsp_tools/utils/xml_utils.py +0 -145
- dsp_tools/utils/xml_validation.py +0 -197
- dsp_tools/utils/xml_validation_models.py +0 -68
- dsp_tools/xmllib/models/file_values.py +0 -78
- dsp_tools/xmllib/models/resource.py +0 -415
- dsp_tools/xmllib/models/values.py +0 -428
- dsp_tools-9.1.0.post11.dist-info/METADATA +0 -130
- dsp_tools-9.1.0.post11.dist-info/RECORD +0 -167
- dsp_tools-9.1.0.post11.dist-info/WHEEL +0 -4
- dsp_tools-9.1.0.post11.dist-info/licenses/LICENSE +0 -674
- /dsp_tools/{commands/excel2json/new_lists → clients}/__init__.py +0 -0
- /dsp_tools/commands/{excel2json/new_lists/models → create}/__init__.py +0 -0
- /dsp_tools/commands/{project → create/create_on_server}/__init__.py +0 -0
- /dsp_tools/commands/{project/create → create/models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → create/parsing}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate → create/serialisation}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/models → excel2json/lists}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/sparql → excel2json/lists/models}/__init__.py +0 -0
- /dsp_tools/commands/excel2json/{new_lists → lists}/models/deserialise.py +0 -0
- /dsp_tools/commands/{xmlupload/models/deserialise → get}/__init__.py +0 -0
- /dsp_tools/commands/{xmlupload/models/serialise → get/legacy_models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → get/legacy_models}/helpers.py +0 -0
- /dsp_tools/{models → commands/get/models}/__init__.py +0 -0
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import itertools
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
|
-
from lxml import etree
|
|
8
|
-
|
|
9
|
-
from dsp_tools.commands.xmlupload.models.deserialise.deserialise_value import IIIFUriInfo
|
|
10
|
-
from dsp_tools.commands.xmlupload.models.deserialise.deserialise_value import XMLBitstream
|
|
11
|
-
from dsp_tools.commands.xmlupload.models.deserialise.deserialise_value import XMLProperty
|
|
12
|
-
from dsp_tools.commands.xmlupload.models.permission import Permissions
|
|
13
|
-
from dsp_tools.models.datetimestamp import DateTimeStamp
|
|
14
|
-
from dsp_tools.models.exceptions import XmlUploadError
|
|
15
|
-
|
|
16
|
-
COMPOSITE_PROPS = (
|
|
17
|
-
"boolean-prop",
|
|
18
|
-
"color-prop",
|
|
19
|
-
"date-prop",
|
|
20
|
-
"decimal-prop",
|
|
21
|
-
"geometry-prop",
|
|
22
|
-
"geoname-prop",
|
|
23
|
-
"integer-prop",
|
|
24
|
-
"list-prop",
|
|
25
|
-
"resptr-prop",
|
|
26
|
-
"text-prop",
|
|
27
|
-
"time-prop",
|
|
28
|
-
"uri-prop",
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@dataclass(frozen=True)
|
|
33
|
-
class BitstreamInfo:
|
|
34
|
-
"""
|
|
35
|
-
Represents a bitstream object,
|
|
36
|
-
consisting of its file name on the local file system,
|
|
37
|
-
the internal file name assigned by the ingest service
|
|
38
|
-
and optionally its permissions.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
local_file: str
|
|
42
|
-
internal_file_name: str
|
|
43
|
-
permissions: Permissions | None = None
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@dataclass(frozen=True)
|
|
47
|
-
class XMLResource:
|
|
48
|
-
"""
|
|
49
|
-
Represents a resource in the XML used for data import.
|
|
50
|
-
|
|
51
|
-
Attributes:
|
|
52
|
-
res_id: The unique id of the resource
|
|
53
|
-
iri: The custom IRI of the resource
|
|
54
|
-
ark: The custom ARK of the resource
|
|
55
|
-
label: The label of the resource
|
|
56
|
-
restype: The type of the resource
|
|
57
|
-
permissions: The reference to the permissions set for this resource
|
|
58
|
-
creation_date: The creation date of the resource
|
|
59
|
-
bitstream: The bitstream object belonging to the resource
|
|
60
|
-
properties: The list of properties of the resource
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
res_id: str
|
|
64
|
-
iri: Optional[str]
|
|
65
|
-
ark: Optional[str]
|
|
66
|
-
label: str
|
|
67
|
-
restype: str
|
|
68
|
-
permissions: Optional[str]
|
|
69
|
-
creation_date: Optional[DateTimeStamp]
|
|
70
|
-
bitstream: Optional[XMLBitstream]
|
|
71
|
-
iiif_uri: Optional[IIIFUriInfo]
|
|
72
|
-
properties: list[XMLProperty]
|
|
73
|
-
|
|
74
|
-
@staticmethod
|
|
75
|
-
def from_node(node: etree._Element, default_ontology: str) -> XMLResource:
|
|
76
|
-
"""
|
|
77
|
-
Factory that parses a resource node from the XML DOM
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
node: The DOM node to be processed representing a resource (which is a child of the `<knora>` element)
|
|
81
|
-
default_ontology: The default ontology (given in the attribute default-ontology of the `<knora>` element)
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
An XMLResource object
|
|
85
|
-
"""
|
|
86
|
-
bitstream, iiif_uri, properties = XMLResource._get_properties(node, default_ontology)
|
|
87
|
-
return XMLResource(
|
|
88
|
-
res_id=node.attrib["id"],
|
|
89
|
-
iri=node.attrib.get("iri"),
|
|
90
|
-
ark=node.attrib.get("ark"),
|
|
91
|
-
label=node.attrib["label"],
|
|
92
|
-
restype=XMLResource._get_restype(node, default_ontology),
|
|
93
|
-
permissions=node.attrib.get("permissions"),
|
|
94
|
-
creation_date=DateTimeStamp(x) if (x := node.attrib.get("creation_date")) else None,
|
|
95
|
-
bitstream=bitstream,
|
|
96
|
-
iiif_uri=iiif_uri,
|
|
97
|
-
properties=properties,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
@staticmethod
|
|
101
|
-
def _get_restype(node: etree._Element, default_ontology: str) -> str:
|
|
102
|
-
# get the resource type which is in format namespace:resourcetype, p.ex. rosetta:Image
|
|
103
|
-
restype_orig = node.attrib["restype"]
|
|
104
|
-
if ":" not in restype_orig:
|
|
105
|
-
return f"knora-api:{restype_orig}"
|
|
106
|
-
elif restype_orig.startswith(":"):
|
|
107
|
-
# replace an empty namespace with the default ontology name
|
|
108
|
-
return f"{default_ontology}:{restype_orig[1:]}"
|
|
109
|
-
else:
|
|
110
|
-
return restype_orig
|
|
111
|
-
|
|
112
|
-
@staticmethod
|
|
113
|
-
def _get_properties(
|
|
114
|
-
node: etree._Element, default_ontology: str
|
|
115
|
-
) -> tuple[XMLBitstream | None, IIIFUriInfo | None, list[XMLProperty]]:
|
|
116
|
-
bitstream: XMLBitstream | None = None
|
|
117
|
-
iiif_uri: IIIFUriInfo | None = None
|
|
118
|
-
ungrouped_properties: list[XMLProperty] = []
|
|
119
|
-
for subnode in node:
|
|
120
|
-
match subnode.tag:
|
|
121
|
-
case "bitstream":
|
|
122
|
-
bitstream = XMLBitstream.from_node(subnode)
|
|
123
|
-
case "iiif-uri":
|
|
124
|
-
iiif_uri = IIIFUriInfo.from_node(subnode)
|
|
125
|
-
case "isSegmentOf" | "relatesTo":
|
|
126
|
-
ungrouped_properties.append(XMLProperty.from_node(subnode, "resptr", default_ontology))
|
|
127
|
-
case "hasSegmentBounds":
|
|
128
|
-
ungrouped_properties.append(XMLProperty.from_node(subnode, "interval", default_ontology))
|
|
129
|
-
case "hasTitle" | "hasComment" | "hasDescription" | "hasKeyword":
|
|
130
|
-
ungrouped_properties.append(XMLProperty.from_node(subnode, "text", default_ontology))
|
|
131
|
-
case str() as x if x in COMPOSITE_PROPS:
|
|
132
|
-
# get the property type which is in format type-prop, p.ex. <decimal-prop>
|
|
133
|
-
prop_type, _ = subnode.tag.split("-")
|
|
134
|
-
ungrouped_properties.append(XMLProperty.from_node(subnode, prop_type, default_ontology))
|
|
135
|
-
case _:
|
|
136
|
-
raise XmlUploadError(f"Unexpected tag '{subnode.tag}'")
|
|
137
|
-
grouped_properties = XMLResource._group_props(ungrouped_properties)
|
|
138
|
-
return bitstream, iiif_uri, grouped_properties
|
|
139
|
-
|
|
140
|
-
@staticmethod
|
|
141
|
-
def _group_props(ungrouped_properties: list[XMLProperty]) -> list[XMLProperty]:
|
|
142
|
-
properties = []
|
|
143
|
-
ungrouped_properties.sort(key=lambda x: x.name)
|
|
144
|
-
for _, xml_props in itertools.groupby(ungrouped_properties, lambda x: x.name):
|
|
145
|
-
if len(xml_props_list := list(xml_props)) == 1:
|
|
146
|
-
properties.append(xml_props_list[0])
|
|
147
|
-
else:
|
|
148
|
-
new_prop = xml_props_list.pop(0)
|
|
149
|
-
for xml_prop in xml_props_list:
|
|
150
|
-
new_prop.values.extend(xml_prop.values)
|
|
151
|
-
properties.append(new_prop)
|
|
152
|
-
return properties
|
|
153
|
-
|
|
154
|
-
def get_props_with_links(self) -> list[XMLProperty]:
|
|
155
|
-
"""
|
|
156
|
-
Get a list of all XMLProperties that have an outgoing link to another resource, be it a resptr-prop link
|
|
157
|
-
or a standoff link in a text.
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
list of all XMLProperties
|
|
161
|
-
"""
|
|
162
|
-
link_properties: list[XMLProperty] = []
|
|
163
|
-
for prop in self.properties:
|
|
164
|
-
if prop.valtype == "resptr":
|
|
165
|
-
link_properties.append(prop)
|
|
166
|
-
elif prop.valtype == "text":
|
|
167
|
-
for value in prop.values:
|
|
168
|
-
if value.resrefs:
|
|
169
|
-
link_properties.append(prop)
|
|
170
|
-
break
|
|
171
|
-
return link_properties
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
from rdflib.namespace import Namespace
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def get_default_json_ld_context() -> dict[str, str]:
|
|
5
|
-
"""
|
|
6
|
-
Returns the JSON-LD context as a dictionary.
|
|
7
|
-
|
|
8
|
-
Returns:
|
|
9
|
-
JSON-LD context as a dictionary.
|
|
10
|
-
"""
|
|
11
|
-
return {
|
|
12
|
-
"knora-api": "http://api.knora.org/ontology/knora-api/v2#",
|
|
13
|
-
"salsah-gui": "http://api.knora.org/ontology/salsah-gui/v2#",
|
|
14
|
-
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
|
15
|
-
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
|
16
|
-
"owl": "http://www.w3.org/2002/07/owl#",
|
|
17
|
-
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def make_namespace_dict_from_onto_names(ontos: dict[str, str]) -> dict[str, Namespace]:
|
|
22
|
-
"""Provided a dictionary of ontology names and IRIs, returns a dictionary of Namespace objects."""
|
|
23
|
-
ontos = correct_project_context_namespaces(ontos)
|
|
24
|
-
namespaces = {k: Namespace(v) for k, v in ontos.items()}
|
|
25
|
-
namespaces["knora-api"] = Namespace("http://api.knora.org/ontology/knora-api/v2#")
|
|
26
|
-
return namespaces
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def correct_project_context_namespaces(ontos: dict[str, str]) -> dict[str, str]:
|
|
30
|
-
"""Add the hashtag to make it a valid namespace."""
|
|
31
|
-
return {k: f"{v}#" for k, v in ontos.items()}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def get_json_ld_context_for_project(ontos: dict[str, str]) -> dict[str, str]:
|
|
35
|
-
"""Provided a dictionary of ontology names and IRIs, returns a JSON-LD context for the project."""
|
|
36
|
-
context = get_default_json_ld_context()
|
|
37
|
-
project_context = correct_project_context_namespaces(ontos)
|
|
38
|
-
context.update(project_context)
|
|
39
|
-
return context
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from dataclasses import field
|
|
3
|
-
from typing import Any
|
|
4
|
-
from typing import Literal
|
|
5
|
-
|
|
6
|
-
import regex
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclass(frozen=True)
|
|
10
|
-
class OntoInfo:
|
|
11
|
-
"""This class saves the properties and the classes from an ontology."""
|
|
12
|
-
|
|
13
|
-
classes: list[str] = field(default_factory=list)
|
|
14
|
-
properties: list[str] = field(default_factory=list)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@dataclass
|
|
18
|
-
class ProjectOntosInformation:
|
|
19
|
-
"""This class saves information needed to check the consistency with the ontology."""
|
|
20
|
-
|
|
21
|
-
default_ontology_prefix: str
|
|
22
|
-
onto_lookup: dict[str, OntoInfo]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def make_project_onto_information(
|
|
26
|
-
default_onto_prefix: str,
|
|
27
|
-
ontologies: dict[str, list[dict[str, Any]]],
|
|
28
|
-
) -> ProjectOntosInformation:
|
|
29
|
-
"""
|
|
30
|
-
This function formats ontologies returned by the dsp-api into a look-up model.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
default_onto_prefix: prefix for the default ontology
|
|
34
|
-
ontologies: ontologies returned from the dsp-api
|
|
35
|
-
|
|
36
|
-
Returns:
|
|
37
|
-
A look-up that contains the property and class names from each ontology
|
|
38
|
-
"""
|
|
39
|
-
onto_dict = {
|
|
40
|
-
onto_name: _extract_classes_and_properties_from_onto(onto_json) for onto_name, onto_json in ontologies.items()
|
|
41
|
-
}
|
|
42
|
-
return ProjectOntosInformation(default_onto_prefix, onto_dict)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _extract_classes_and_properties_from_onto(onto_json: list[dict[str, Any]]) -> OntoInfo:
|
|
46
|
-
"""
|
|
47
|
-
This function takes an ontology response from the DSP-API.
|
|
48
|
-
It extracts the classes and properties.
|
|
49
|
-
And saves them in an instance of the class Ontology.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
onto_json: response from DSP-API
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
Ontology instance with the classes and properties
|
|
56
|
-
"""
|
|
57
|
-
classes = _get_all_cleaned_classes_from_onto(onto_json)
|
|
58
|
-
properties = _get_all_cleaned_properties_from_onto(onto_json)
|
|
59
|
-
return OntoInfo(classes, properties)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def _get_all_cleaned_classes_from_onto(onto_json: list[dict[str, Any]]) -> list[str]:
|
|
63
|
-
classes = _get_all_classes_from_onto(onto_json)
|
|
64
|
-
return _remove_prefixes(classes)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def _get_all_classes_from_onto(onto_json: list[dict[str, Any]]) -> list[str]:
|
|
68
|
-
return [elem["@id"] for elem in onto_json if elem.get("knora-api:isResourceClass")]
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def _get_all_cleaned_properties_from_onto(onto_json: list[dict[str, Any]]) -> list[str]:
|
|
72
|
-
props = _get_all_properties_from_onto(onto_json)
|
|
73
|
-
return _remove_prefixes(props)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _get_all_properties_from_onto(onto_json: list[dict[str, Any]]) -> list[str]:
|
|
77
|
-
return [elem["@id"] for elem in onto_json if not elem.get("knora-api:isResourceClass")]
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def _remove_prefixes(ontology_elements: list[str]) -> list[str]:
|
|
81
|
-
return [x.split(":")[1] for x in ontology_elements]
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
AllowedEncodings = Literal["utf8", "xml"]
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@dataclass
|
|
88
|
-
class TextValueData:
|
|
89
|
-
resource_id: str
|
|
90
|
-
res_type: str
|
|
91
|
-
property_name: str
|
|
92
|
-
encoding: AllowedEncodings
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@dataclass
|
|
96
|
-
class PropertyTextValueTypes:
|
|
97
|
-
"""
|
|
98
|
-
This class contains the information
|
|
99
|
-
which properties have which type of encoding for a TextValue property in the ontology.
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
formatted_text_props: set[str] = field(default_factory=set)
|
|
103
|
-
unformatted_text_props: set[str] = field(default_factory=set)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def get_text_value_types_of_properties_from_onto(
|
|
107
|
-
onto_json_dict: dict[str, list[dict[str, Any]]], default_onto: str
|
|
108
|
-
) -> PropertyTextValueTypes:
|
|
109
|
-
"""
|
|
110
|
-
This function takes a dict with the project ontologies in the format:
|
|
111
|
-
{ ontology_name: ontology_json_from_api }
|
|
112
|
-
It retrieves the properties that are used with `knora-api:TextValue`.
|
|
113
|
-
They are separated into two categories: xml encoded ones, and utf8 encoded ones.
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
onto_json_dict: dict with the project ontologies
|
|
117
|
-
default_onto: name of the default ontology
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
Look-up containing the properties separated according to the formatting
|
|
121
|
-
"""
|
|
122
|
-
all_props = []
|
|
123
|
-
for onto_json in onto_json_dict.values():
|
|
124
|
-
all_props.extend(_get_all_text_value_types_properties_and_from_onto(onto_json))
|
|
125
|
-
return _make_text_value_property_type_lookup(all_props, default_onto)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _make_text_value_property_type_lookup(
|
|
129
|
-
prop_list: list[tuple[str, str]], default_onto: str
|
|
130
|
-
) -> PropertyTextValueTypes:
|
|
131
|
-
formatted_text = {
|
|
132
|
-
_remove_default_prefix(p, default_onto) for p, _type in prop_list if _type == "salsah-gui:Richtext"
|
|
133
|
-
}
|
|
134
|
-
formatted_text.update(["hasComment", "hasDescription"]) # knora-api properties that can be used directly
|
|
135
|
-
unformatted_text = {
|
|
136
|
-
_remove_default_prefix(p, default_onto) for p, _type in prop_list if _type != "salsah-gui:Richtext"
|
|
137
|
-
}
|
|
138
|
-
unformatted_text.update(["hasTitle", "hasKeyword"]) # knora-api properties that can be used directly
|
|
139
|
-
return PropertyTextValueTypes(formatted_text, unformatted_text)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def _remove_default_prefix(prop_str: str, default_onto: str) -> str:
|
|
143
|
-
return regex.sub(rf"^{default_onto}:", ":", prop_str)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def _get_all_text_value_types_properties_and_from_onto(onto_json: list[dict[str, Any]]) -> list[tuple[str, str]]:
|
|
147
|
-
prop_id_list = [elem["@id"] for elem in onto_json if _check_if_text_value_property(elem)]
|
|
148
|
-
type_list = [elem["salsah-gui:guiElement"]["@id"] for elem in onto_json if _check_if_text_value_property(elem)]
|
|
149
|
-
return list(zip(prop_id_list, type_list))
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def _check_if_text_value_property(onto_ele: dict[str, Any]) -> bool:
|
|
153
|
-
if not onto_ele.get("knora-api:isResourceProperty"):
|
|
154
|
-
return False
|
|
155
|
-
if not (object_type := onto_ele.get("knora-api:objectType")):
|
|
156
|
-
return False
|
|
157
|
-
match object_type["@id"]:
|
|
158
|
-
case "knora-api:TextValue":
|
|
159
|
-
return True
|
|
160
|
-
case _:
|
|
161
|
-
return False
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import itertools
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
import pandas as pd
|
|
5
|
-
|
|
6
|
-
from dsp_tools.commands.xmlupload.models.ontology_lookup_models import TextValueData
|
|
7
|
-
|
|
8
|
-
separator = "\n "
|
|
9
|
-
list_separator = "\n - "
|
|
10
|
-
medium_separator = "\n\n"
|
|
11
|
-
maximum_prints = 50
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass(frozen=True)
|
|
15
|
-
class InvalidOntologyElementsInData:
|
|
16
|
-
"""This class saves and prints out the information regarding ontology classes and properties
|
|
17
|
-
that are in the XML but not the ontology."""
|
|
18
|
-
|
|
19
|
-
classes: list[tuple[str, list[str], str]]
|
|
20
|
-
properties: list[tuple[str, list[str], str]]
|
|
21
|
-
ontos_on_server: list[str]
|
|
22
|
-
|
|
23
|
-
def execute_problem_protocol(self) -> tuple[str, pd.DataFrame | None]:
|
|
24
|
-
"""
|
|
25
|
-
If there are any elements in properties or classes,
|
|
26
|
-
this method composes an error message.
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
the error message and a dataframe with the errors if they exceed 50 or None
|
|
30
|
-
"""
|
|
31
|
-
base_msg = (
|
|
32
|
-
f"\nSome property and/or class type(s) used in the XML are unknown.\n"
|
|
33
|
-
f"The ontologies for your project on the server are:{list_separator}"
|
|
34
|
-
f"{list_separator.join(self.ontos_on_server)}{medium_separator}"
|
|
35
|
-
)
|
|
36
|
-
if cls_msg := self._compose_problem_string_for_cls():
|
|
37
|
-
base_msg += cls_msg + "\n"
|
|
38
|
-
if prop_msg := self._compose_problem_string_for_props():
|
|
39
|
-
base_msg += prop_msg + "\n"
|
|
40
|
-
if (
|
|
41
|
-
self._calculate_num_resources(self.classes) + self._calculate_num_resources(self.properties)
|
|
42
|
-
> maximum_prints
|
|
43
|
-
):
|
|
44
|
-
df = self._get_problems_as_df()
|
|
45
|
-
return base_msg, df
|
|
46
|
-
return base_msg, None
|
|
47
|
-
|
|
48
|
-
def _get_problems_as_df(self) -> pd.DataFrame:
|
|
49
|
-
problems = [
|
|
50
|
-
[
|
|
51
|
-
{
|
|
52
|
-
"problematic type": probs[0],
|
|
53
|
-
"resource id": x,
|
|
54
|
-
"problem": probs[2],
|
|
55
|
-
}
|
|
56
|
-
for x in probs[1]
|
|
57
|
-
]
|
|
58
|
-
for probs in self.classes
|
|
59
|
-
]
|
|
60
|
-
problems.extend(
|
|
61
|
-
[
|
|
62
|
-
[
|
|
63
|
-
{
|
|
64
|
-
"problematic type": probs[0],
|
|
65
|
-
"resource id": x,
|
|
66
|
-
"problem": probs[2],
|
|
67
|
-
}
|
|
68
|
-
for x in probs[1]
|
|
69
|
-
]
|
|
70
|
-
for probs in self.properties
|
|
71
|
-
]
|
|
72
|
-
)
|
|
73
|
-
unpacked: list[dict[str, str]] = list(itertools.chain(*problems))
|
|
74
|
-
return pd.DataFrame.from_records(unpacked)
|
|
75
|
-
|
|
76
|
-
def _calculate_num_resources(self, to_count: list[tuple[str, list[str], str]]) -> int:
|
|
77
|
-
return sum((len(x[1]) for x in to_count))
|
|
78
|
-
|
|
79
|
-
def _compose_problem_string_for_cls(self) -> str | None:
|
|
80
|
-
if self.classes:
|
|
81
|
-
if self._calculate_num_resources(self.classes) > maximum_prints:
|
|
82
|
-
return "Many resources have an invalid resource type.\nPlease consult the file for details."
|
|
83
|
-
|
|
84
|
-
def _format_cls(cls_tup: tuple[str, list[str], str]) -> str:
|
|
85
|
-
ids = list_separator + list_separator.join(cls_tup[1])
|
|
86
|
-
return (
|
|
87
|
-
f" Resource Type: '{cls_tup[0]}'{separator}"
|
|
88
|
-
f"Problem: '{cls_tup[2]}'{separator}"
|
|
89
|
-
f"Resource ID(s):{ids}"
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
problems = [_format_cls(x) for x in self.classes]
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
f"The following resource(s) have an invalid resource type:{medium_separator}"
|
|
96
|
-
+ medium_separator.join(problems)
|
|
97
|
-
+ "\n"
|
|
98
|
-
)
|
|
99
|
-
else:
|
|
100
|
-
return None
|
|
101
|
-
|
|
102
|
-
def _compose_problem_string_for_props(self) -> str | None:
|
|
103
|
-
if self.properties:
|
|
104
|
-
if self._calculate_num_resources(self.properties) > maximum_prints:
|
|
105
|
-
return "Many properties have an invalid resource type.\nPlease consult the file for details."
|
|
106
|
-
|
|
107
|
-
def _format_prop(prop_tup: tuple[str, list[str], str]) -> str:
|
|
108
|
-
ids = list_separator + list_separator.join(prop_tup[1])
|
|
109
|
-
return (
|
|
110
|
-
f" Property Name: '{prop_tup[0]}'{separator}"
|
|
111
|
-
f"Problem: '{prop_tup[2]}'{separator}"
|
|
112
|
-
f"Resource ID(s):{ids}"
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
problems = [_format_prop(x) for x in self.properties]
|
|
116
|
-
return (
|
|
117
|
-
f"The following resource(s) have invalid property type(s):{medium_separator}"
|
|
118
|
-
+ medium_separator.join(problems)
|
|
119
|
-
+ "\n"
|
|
120
|
-
)
|
|
121
|
-
else:
|
|
122
|
-
return None
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
@dataclass
|
|
126
|
-
class InvalidTextValueEncodings:
|
|
127
|
-
"""
|
|
128
|
-
This class contains information about resources and the respective properties that have invalid text encodings.
|
|
129
|
-
|
|
130
|
-
An invalid encoding would be a property that specifies `knora-api:Richtext` in the ontology,
|
|
131
|
-
but the `<text>` elements use: `<text encoding="utf8">`.
|
|
132
|
-
OR
|
|
133
|
-
A property that specifies `knora-api:Textarea` or `knora-api:SimpleText`
|
|
134
|
-
but the `<text>` elements use: `<text encoding="xml">`.
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
-
problematic_resources: list[TextValueData]
|
|
138
|
-
|
|
139
|
-
def execute_problem_protocol(self) -> tuple[str, pd.DataFrame | None]:
|
|
140
|
-
"""
|
|
141
|
-
This method composes an error message for the user.
|
|
142
|
-
|
|
143
|
-
Returns:
|
|
144
|
-
the error message and a dataframe with the errors if they exceed the maximum allowed print statements
|
|
145
|
-
"""
|
|
146
|
-
base_msg = (
|
|
147
|
-
"\nSome text encodings used in the XML data file are not conform with the gui_element "
|
|
148
|
-
"specified in the JSON ontology.\n"
|
|
149
|
-
"Please consult the ontology regarding the assigned gui_elements."
|
|
150
|
-
)
|
|
151
|
-
df = self._get_problems_as_df()
|
|
152
|
-
if len(df) > maximum_prints:
|
|
153
|
-
return base_msg, df
|
|
154
|
-
return base_msg + medium_separator + _make_msg_from_df(df), None
|
|
155
|
-
|
|
156
|
-
def _get_problems_as_df(self) -> pd.DataFrame:
|
|
157
|
-
df = pd.DataFrame(
|
|
158
|
-
{
|
|
159
|
-
"Resource ID": [x.resource_id for x in self.problematic_resources],
|
|
160
|
-
"Resource Type": [x.res_type for x in self.problematic_resources],
|
|
161
|
-
"Property Name": [x.property_name for x in self.problematic_resources],
|
|
162
|
-
"Encoding Used": [x.encoding for x in self.problematic_resources],
|
|
163
|
-
}
|
|
164
|
-
)
|
|
165
|
-
return df.sort_values(by=["Resource Type", "Resource ID", "Property Name"], ignore_index=True)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def _make_msg_from_df(df: pd.DataFrame) -> str:
|
|
169
|
-
groups = df.groupby(by="Resource ID")
|
|
170
|
-
return medium_separator.join([_make_msg_for_one_resource(str(_id), res_df) for _id, res_df in groups])
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def _make_msg_for_one_resource(res_id: str, res_df: pd.DataFrame) -> str:
|
|
174
|
-
props = res_df["Property Name"].tolist()
|
|
175
|
-
encding = res_df["Encoding Used"].tolist()
|
|
176
|
-
restype = next(iter(res_df["Resource Type"]))
|
|
177
|
-
problems = [f"Property Name: '{p}' -> Encoding Used: '{e}'" for p, e in zip(props, encding)]
|
|
178
|
-
return f"Resource ID: '{res_id}' | Resource Type: '{restype}'{list_separator}{list_separator.join(problems)}"
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
from pyld import jsonld
|
|
5
|
-
from rdflib import Graph
|
|
6
|
-
from rdflib import URIRef
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def serialise_property_graph(rdf_graph: Graph, prop_name: URIRef) -> dict[str, Any]:
|
|
10
|
-
"""
|
|
11
|
-
It serialises an RDF-graph into json-ld,
|
|
12
|
-
which is conform to the format expected by the DSP-API.
|
|
13
|
-
It returns the information about the property and its value(s).
|
|
14
|
-
It is possible that the graph contains different properties.
|
|
15
|
-
The properties do not have to be from the same ontology.
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
rdf_graph: RDF graph
|
|
19
|
-
prop_name: One property name used in the graph.
|
|
20
|
-
If there are several it does not matter which.
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
A json-ld
|
|
24
|
-
"""
|
|
25
|
-
json_graph = _make_json(rdf_graph)
|
|
26
|
-
return _frame_property(json_graph, prop_name)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _frame_property(serialised_json: list[dict[str, Any]], prop_name: URIRef) -> dict[str, Any]:
|
|
30
|
-
json_frame: dict[str, Any] = {
|
|
31
|
-
str(prop_name): {},
|
|
32
|
-
}
|
|
33
|
-
framed: dict[str, Any] = jsonld.frame(serialised_json, json_frame)
|
|
34
|
-
return framed
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _make_json(rdf_graph: Graph) -> list[dict[str, Any]]:
|
|
38
|
-
graph_bytes = rdf_graph.serialize(format="json-ld", encoding="utf-8")
|
|
39
|
-
json_graph: list[dict[str, Any]] = json.loads(graph_bytes)
|
|
40
|
-
return json_graph
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any
|
|
5
|
-
from typing import Protocol
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@dataclass(frozen=True)
|
|
9
|
-
class SerialiseProperty:
|
|
10
|
-
property_name: str
|
|
11
|
-
values: list[SerialiseValue]
|
|
12
|
-
|
|
13
|
-
def serialise(self) -> dict[str, Any]:
|
|
14
|
-
"""Serialise the property and all its values."""
|
|
15
|
-
return {self.property_name: [value.serialise() for value in self.values]}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class SerialiseValue(Protocol):
|
|
19
|
-
"""A value to be serialised."""
|
|
20
|
-
|
|
21
|
-
value: str
|
|
22
|
-
permissions: str | None
|
|
23
|
-
comment: str | None
|
|
24
|
-
|
|
25
|
-
def serialise(self) -> dict[str, Any]:
|
|
26
|
-
"""Serialise the value."""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@dataclass(frozen=True)
|
|
30
|
-
class SerialiseURI(SerialiseValue):
|
|
31
|
-
"""A URI to be serialised."""
|
|
32
|
-
|
|
33
|
-
value: str
|
|
34
|
-
permissions: str | None
|
|
35
|
-
comment: str | None
|
|
36
|
-
|
|
37
|
-
def serialise(self) -> dict[str, Any]:
|
|
38
|
-
"""Serialise the URI value."""
|
|
39
|
-
|
|
40
|
-
serialised = {
|
|
41
|
-
"@type": "knora-api:UriValue",
|
|
42
|
-
"knora-api:uriValueAsUri": {
|
|
43
|
-
"@type": "xsd:anyURI",
|
|
44
|
-
"@value": self.value,
|
|
45
|
-
},
|
|
46
|
-
}
|
|
47
|
-
if self.comment:
|
|
48
|
-
serialised["knora-api:valueHasComment"] = self.comment
|
|
49
|
-
if self.permissions:
|
|
50
|
-
serialised["knora-api:hasPermissions"] = self.permissions
|
|
51
|
-
return serialised
|