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,507 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from rdflib import RDF
|
|
5
|
+
from rdflib import RDFS
|
|
6
|
+
from rdflib import SH
|
|
7
|
+
from rdflib import XSD
|
|
8
|
+
from rdflib import Graph
|
|
9
|
+
from rdflib import Literal
|
|
10
|
+
|
|
11
|
+
from dsp_tools.commands.validate_data.constants import FILE_VALUE_PROPERTIES
|
|
12
|
+
from dsp_tools.commands.validate_data.constants import LEGAL_INFO_PROPS
|
|
13
|
+
from dsp_tools.commands.validate_data.models.input_problems import AllProblems
|
|
14
|
+
from dsp_tools.commands.validate_data.models.validation import DetailBaseInfo
|
|
15
|
+
from dsp_tools.commands.validate_data.models.validation import QueryInfo
|
|
16
|
+
from dsp_tools.commands.validate_data.models.validation import UnexpectedComponent
|
|
17
|
+
from dsp_tools.commands.validate_data.models.validation import ValidationReportGraphs
|
|
18
|
+
from dsp_tools.commands.validate_data.models.validation import ValidationResult
|
|
19
|
+
from dsp_tools.commands.validate_data.models.validation import ValidationResultBaseInfo
|
|
20
|
+
from dsp_tools.commands.validate_data.models.validation import ViolationType
|
|
21
|
+
from dsp_tools.commands.validate_data.process_validation_report.reformat_validation_results import (
|
|
22
|
+
reformat_extracted_results,
|
|
23
|
+
)
|
|
24
|
+
from dsp_tools.utils.rdf_constants import API_SHAPES
|
|
25
|
+
from dsp_tools.utils.rdf_constants import DASH
|
|
26
|
+
from dsp_tools.utils.rdf_constants import KNORA_API
|
|
27
|
+
from dsp_tools.utils.rdf_constants import SubjectObjectTypeAlias
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def reformat_validation_graph(report: ValidationReportGraphs) -> AllProblems:
|
|
31
|
+
"""
|
|
32
|
+
Reformats the validation result from an RDF graph into class instances
|
|
33
|
+
that are used to communicate the problems with the user.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
report: with all the information necessary to construct a user message
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
All Problems
|
|
40
|
+
"""
|
|
41
|
+
logger.debug("Reformatting validation results.")
|
|
42
|
+
results_and_onto = report.validation_graph + report.onto_graph
|
|
43
|
+
data_and_onto = report.onto_graph + report.data_graph
|
|
44
|
+
validation_results, unexpected_extracted = _query_all_results(results_and_onto, data_and_onto)
|
|
45
|
+
reformatted_results = reformat_extracted_results(validation_results)
|
|
46
|
+
return AllProblems(reformatted_results, unexpected_extracted)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _query_all_results(
|
|
50
|
+
results_and_onto: Graph, data_onto_graph: Graph
|
|
51
|
+
) -> tuple[list[ValidationResult], list[UnexpectedComponent]]:
|
|
52
|
+
no_details, with_details = _separate_result_types(results_and_onto, data_onto_graph)
|
|
53
|
+
extracted_results: list[ValidationResult] = []
|
|
54
|
+
unexpected_components: list[UnexpectedComponent] = []
|
|
55
|
+
|
|
56
|
+
no_detail_extracted, no_detail_unexpected = _query_all_without_detail(no_details, results_and_onto, data_onto_graph)
|
|
57
|
+
extracted_results.extend(no_detail_extracted)
|
|
58
|
+
unexpected_components.extend(no_detail_unexpected)
|
|
59
|
+
|
|
60
|
+
detail_reformatted, detail_unexpected = _query_all_with_detail(with_details, results_and_onto, data_onto_graph)
|
|
61
|
+
extracted_results.extend(detail_reformatted)
|
|
62
|
+
unexpected_components.extend(detail_unexpected)
|
|
63
|
+
return extracted_results, unexpected_components
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _separate_result_types(
|
|
67
|
+
results_and_onto: Graph, data_onto_graph: Graph
|
|
68
|
+
) -> tuple[list[ValidationResultBaseInfo], list[ValidationResultBaseInfo]]:
|
|
69
|
+
base_info = _extract_base_info_of_resource_results(results_and_onto, data_onto_graph)
|
|
70
|
+
no_details = [x for x in base_info if not x.detail]
|
|
71
|
+
with_details = [x for x in base_info if x.detail]
|
|
72
|
+
return no_details, with_details
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _extract_base_info_of_resource_results(
|
|
76
|
+
results_and_onto: Graph, data_onto_graph: Graph
|
|
77
|
+
) -> list[ValidationResultBaseInfo]:
|
|
78
|
+
all_res_focus_nodes = []
|
|
79
|
+
main_bns = _get_all_main_result_bns(results_and_onto)
|
|
80
|
+
value_types = _get_all_value_classes(data_onto_graph)
|
|
81
|
+
for nd in main_bns:
|
|
82
|
+
focus_iri = next(results_and_onto.objects(nd, SH.focusNode))
|
|
83
|
+
res_type = next(data_onto_graph.objects(focus_iri, RDF.type))
|
|
84
|
+
info = QueryInfo(
|
|
85
|
+
validation_bn=nd,
|
|
86
|
+
focus_iri=focus_iri,
|
|
87
|
+
focus_rdf_type=res_type,
|
|
88
|
+
)
|
|
89
|
+
all_res_focus_nodes.extend(_extract_one_base_info(info, results_and_onto, data_onto_graph, value_types))
|
|
90
|
+
return all_res_focus_nodes
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _get_all_value_classes(data_onto_graph: Graph) -> set[SubjectObjectTypeAlias]:
|
|
94
|
+
all_types = set(data_onto_graph.objects(predicate=RDF.type))
|
|
95
|
+
all_value_types = set()
|
|
96
|
+
for type_ in all_types:
|
|
97
|
+
super_classes = set(data_onto_graph.transitive_objects(type_, RDFS.subClassOf))
|
|
98
|
+
if KNORA_API.Value in super_classes:
|
|
99
|
+
all_value_types.add(type_)
|
|
100
|
+
return all_value_types
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _extract_one_base_info(
|
|
104
|
+
info: QueryInfo, results_and_onto: Graph, data_onto_graph: Graph, value_types: set[SubjectObjectTypeAlias]
|
|
105
|
+
) -> list[ValidationResultBaseInfo]:
|
|
106
|
+
results = []
|
|
107
|
+
path = next(results_and_onto.objects(info.validation_bn, SH.resultPath))
|
|
108
|
+
main_component_type = next(results_and_onto.objects(info.validation_bn, SH.sourceConstraintComponent))
|
|
109
|
+
severity = next(results_and_onto.objects(info.validation_bn, SH.resultSeverity))
|
|
110
|
+
if detail_bn_list := list(results_and_onto.objects(info.validation_bn, SH.detail)):
|
|
111
|
+
for single_detail in detail_bn_list:
|
|
112
|
+
detail_component = next(results_and_onto.objects(single_detail, SH.sourceConstraintComponent))
|
|
113
|
+
detail = DetailBaseInfo(
|
|
114
|
+
detail_bn=single_detail,
|
|
115
|
+
source_constraint_component=detail_component,
|
|
116
|
+
)
|
|
117
|
+
results.append(
|
|
118
|
+
ValidationResultBaseInfo(
|
|
119
|
+
result_bn=info.validation_bn,
|
|
120
|
+
source_constraint_component=main_component_type,
|
|
121
|
+
focus_node_iri=info.focus_iri,
|
|
122
|
+
focus_node_type=info.focus_rdf_type,
|
|
123
|
+
result_path=path,
|
|
124
|
+
severity=severity,
|
|
125
|
+
detail=detail,
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
resource_iri, resource_type, user_facing_prop = _get_resource_iri_and_type(
|
|
130
|
+
info, path, data_onto_graph, value_types
|
|
131
|
+
)
|
|
132
|
+
results.append(
|
|
133
|
+
ValidationResultBaseInfo(
|
|
134
|
+
result_bn=info.validation_bn,
|
|
135
|
+
source_constraint_component=main_component_type,
|
|
136
|
+
focus_node_iri=resource_iri,
|
|
137
|
+
focus_node_type=resource_type,
|
|
138
|
+
result_path=user_facing_prop,
|
|
139
|
+
severity=severity,
|
|
140
|
+
detail=None,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
return results
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _get_all_main_result_bns(results_and_onto: Graph) -> set[SubjectObjectTypeAlias]:
|
|
147
|
+
all_bns = set(results_and_onto.subjects(RDF.type, SH.ValidationResult))
|
|
148
|
+
# All the blank nodes that are referenced in a sh:detail will be queried together with the main validation result
|
|
149
|
+
# if we queried them separately we would get duplicate errors
|
|
150
|
+
detail_bns = set(results_and_onto.objects(predicate=SH.detail))
|
|
151
|
+
return all_bns - detail_bns
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _get_resource_iri_and_type(
|
|
155
|
+
info: QueryInfo, path: SubjectObjectTypeAlias, data_onto_graph: Graph, value_types: set[SubjectObjectTypeAlias]
|
|
156
|
+
) -> tuple[SubjectObjectTypeAlias, SubjectObjectTypeAlias, SubjectObjectTypeAlias]:
|
|
157
|
+
resource_iri, resource_type, user_facing_prop = info.focus_iri, info.focus_rdf_type, path
|
|
158
|
+
if info.focus_rdf_type in value_types:
|
|
159
|
+
resource_iri, predicate = next(data_onto_graph.subject_predicates(object=info.focus_iri))
|
|
160
|
+
resource_type = next(data_onto_graph.objects(resource_iri, RDF.type))
|
|
161
|
+
if user_facing_prop not in LEGAL_INFO_PROPS:
|
|
162
|
+
user_facing_prop = predicate
|
|
163
|
+
return resource_iri, resource_type, user_facing_prop
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _query_all_without_detail(
|
|
167
|
+
all_base_info: list[ValidationResultBaseInfo], results_and_onto: Graph, data: Graph
|
|
168
|
+
) -> tuple[list[ValidationResult], list[UnexpectedComponent]]:
|
|
169
|
+
extracted_results: list[ValidationResult] = []
|
|
170
|
+
unexpected_components: list[UnexpectedComponent] = []
|
|
171
|
+
|
|
172
|
+
for base_info in all_base_info:
|
|
173
|
+
res = _query_one_without_detail(base_info, results_and_onto, data)
|
|
174
|
+
if res is None:
|
|
175
|
+
pass
|
|
176
|
+
elif isinstance(res, UnexpectedComponent):
|
|
177
|
+
unexpected_components.append(res)
|
|
178
|
+
else:
|
|
179
|
+
extracted_results.append(res)
|
|
180
|
+
return extracted_results, unexpected_components
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _query_one_without_detail( # noqa:PLR0911 (Too many return statements)
|
|
184
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph
|
|
185
|
+
) -> ValidationResult | UnexpectedComponent | None:
|
|
186
|
+
msg = next(results_and_onto.objects(base_info.result_bn, SH.resultMessage))
|
|
187
|
+
component = next(results_and_onto.objects(base_info.result_bn, SH.sourceConstraintComponent))
|
|
188
|
+
match component:
|
|
189
|
+
case SH.PatternConstraintComponent:
|
|
190
|
+
return _query_pattern_constraint_component_violation(base_info.result_bn, base_info, results_and_onto)
|
|
191
|
+
case SH.MinCountConstraintComponent:
|
|
192
|
+
return _query_for_min_cardinality_violation(base_info, msg)
|
|
193
|
+
case SH.MaxCountConstraintComponent:
|
|
194
|
+
return ValidationResult(
|
|
195
|
+
violation_type=ViolationType.MAX_CARD,
|
|
196
|
+
res_iri=base_info.focus_node_iri,
|
|
197
|
+
res_class=base_info.focus_node_type,
|
|
198
|
+
severity=base_info.severity,
|
|
199
|
+
property=base_info.result_path,
|
|
200
|
+
expected=msg,
|
|
201
|
+
)
|
|
202
|
+
case SH.LessThanOrEqualsConstraintComponent:
|
|
203
|
+
return _query_for_less_than_or_equal_violation(base_info, results_and_onto, data, msg)
|
|
204
|
+
case DASH.ClosedByTypesConstraintComponent:
|
|
205
|
+
return _query_for_non_existent_cardinality_violation(base_info, results_and_onto, data)
|
|
206
|
+
case SH.SPARQLConstraintComponent:
|
|
207
|
+
return _query_for_unique_value_violation(base_info, results_and_onto)
|
|
208
|
+
case DASH.CoExistsWithConstraintComponent:
|
|
209
|
+
return _query_for_coexists_with_violation(base_info, results_and_onto, data, msg)
|
|
210
|
+
case SH.ClassConstraintComponent:
|
|
211
|
+
return _query_class_constraint_without_detail(base_info, results_and_onto, data, msg)
|
|
212
|
+
case (
|
|
213
|
+
SH.InConstraintComponent
|
|
214
|
+
| SH.LessThanConstraintComponent
|
|
215
|
+
| SH.MinExclusiveConstraintComponent
|
|
216
|
+
| SH.MinInclusiveConstraintComponent
|
|
217
|
+
| DASH.SingleLineConstraintComponent
|
|
218
|
+
| SH.DatatypeConstraintComponent
|
|
219
|
+
):
|
|
220
|
+
return _query_general_violation_info(
|
|
221
|
+
base_info.result_bn, base_info, results_and_onto, ViolationType.GENERIC
|
|
222
|
+
)
|
|
223
|
+
case SH.OrConstraintComponent:
|
|
224
|
+
return _query_general_violation_info_with_value_as_string(
|
|
225
|
+
base_info.result_bn, base_info, results_and_onto, data
|
|
226
|
+
)
|
|
227
|
+
case _:
|
|
228
|
+
return UnexpectedComponent(str(component))
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _query_class_constraint_without_detail(
|
|
232
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph, message: SubjectObjectTypeAlias
|
|
233
|
+
) -> ValidationResult | None:
|
|
234
|
+
val: None | SubjectObjectTypeAlias = next(results_and_onto.objects(base_info.result_bn, SH.value))
|
|
235
|
+
# In this case we have some kind of FileValue violation
|
|
236
|
+
violation_type = ViolationType.GENERIC
|
|
237
|
+
value_type: None | SubjectObjectTypeAlias = None
|
|
238
|
+
msg: None | SubjectObjectTypeAlias = message
|
|
239
|
+
expected = None
|
|
240
|
+
val_type_list = list(data.objects(val, RDF.type))
|
|
241
|
+
# Here we have a normal value type violation
|
|
242
|
+
if val_type_list:
|
|
243
|
+
val_type = val_type_list.pop(0)
|
|
244
|
+
value_super_class = list(results_and_onto.transitive_objects(val_type, RDFS.subClassOf))
|
|
245
|
+
if KNORA_API.FileValue not in value_super_class:
|
|
246
|
+
value_type = val_type
|
|
247
|
+
val = None
|
|
248
|
+
violation_type = ViolationType.VALUE_TYPE
|
|
249
|
+
msg = None
|
|
250
|
+
expected = message
|
|
251
|
+
elif base_info.result_path == KNORA_API.hasStandoffLinkTo:
|
|
252
|
+
violation_type = ViolationType.LINK_TARGET
|
|
253
|
+
return ValidationResult(
|
|
254
|
+
violation_type=violation_type,
|
|
255
|
+
res_iri=base_info.focus_node_iri,
|
|
256
|
+
res_class=base_info.focus_node_type,
|
|
257
|
+
severity=base_info.severity,
|
|
258
|
+
property=base_info.result_path,
|
|
259
|
+
message=msg,
|
|
260
|
+
expected=expected,
|
|
261
|
+
input_value=val,
|
|
262
|
+
input_type=value_type,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _query_for_less_than_or_equal_violation(
|
|
267
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph, message: SubjectObjectTypeAlias
|
|
268
|
+
) -> ValidationResult | None:
|
|
269
|
+
value_iri = next(results_and_onto.objects(base_info.result_bn, SH.focusNode))
|
|
270
|
+
start = next(data.objects(value_iri, API_SHAPES.dateHasStart))
|
|
271
|
+
end = next(data.objects(value_iri, API_SHAPES.dateHasEnd))
|
|
272
|
+
start_lit = cast(Literal, start)
|
|
273
|
+
end_lit = cast(Literal, end)
|
|
274
|
+
start_is_string = start_lit.datatype == XSD.string
|
|
275
|
+
end_is_string = end_lit.datatype == XSD.string
|
|
276
|
+
# If any one of the date ranges cannot be parsed as an xsd date, we get this violation also.
|
|
277
|
+
# But the main problem is, that the date format is wrong, in which case the datatype is xsd:string.
|
|
278
|
+
# This produces its own message
|
|
279
|
+
if any([start_is_string, end_is_string]):
|
|
280
|
+
return None
|
|
281
|
+
return ValidationResult(
|
|
282
|
+
violation_type=ViolationType.GENERIC,
|
|
283
|
+
res_iri=base_info.focus_node_iri,
|
|
284
|
+
res_class=base_info.focus_node_type,
|
|
285
|
+
severity=base_info.severity,
|
|
286
|
+
property=base_info.result_path,
|
|
287
|
+
input_value=_get_value_as_string(base_info.result_bn, results_and_onto, data),
|
|
288
|
+
message=message,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _query_for_non_existent_cardinality_violation(
|
|
293
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph
|
|
294
|
+
) -> ValidationResult | None:
|
|
295
|
+
input_val = None
|
|
296
|
+
if base_info.result_path in FILE_VALUE_PROPERTIES:
|
|
297
|
+
violation_type = ViolationType.FILE_VALUE_PROHIBITED
|
|
298
|
+
if value_bn_found := list(results_and_onto.objects(base_info.result_bn, SH.value)):
|
|
299
|
+
value_bn = value_bn_found.pop(0)
|
|
300
|
+
if file_path := list(data.objects(value_bn, KNORA_API.fileValueHasFilename)):
|
|
301
|
+
input_val = file_path.pop(0)
|
|
302
|
+
elif iiif_uri := list(data.objects(value_bn, KNORA_API.stillImageFileValueHasExternalUrl)):
|
|
303
|
+
input_val = iiif_uri.pop(0)
|
|
304
|
+
else:
|
|
305
|
+
violation_type = ViolationType.NON_EXISTING_CARD
|
|
306
|
+
return ValidationResult(
|
|
307
|
+
violation_type=violation_type,
|
|
308
|
+
res_iri=base_info.focus_node_iri,
|
|
309
|
+
res_class=base_info.focus_node_type,
|
|
310
|
+
severity=base_info.severity,
|
|
311
|
+
property=base_info.result_path,
|
|
312
|
+
input_value=input_val,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _query_all_with_detail(
|
|
317
|
+
all_base_info: list[ValidationResultBaseInfo], results_and_onto: Graph, data_onto_graph: Graph
|
|
318
|
+
) -> tuple[list[ValidationResult], list[UnexpectedComponent]]:
|
|
319
|
+
extracted_results: list[ValidationResult] = []
|
|
320
|
+
unexpected_components: list[UnexpectedComponent] = []
|
|
321
|
+
|
|
322
|
+
for base_info in all_base_info:
|
|
323
|
+
res = _query_one_with_detail(base_info, results_and_onto, data_onto_graph)
|
|
324
|
+
if isinstance(res, UnexpectedComponent):
|
|
325
|
+
unexpected_components.append(res)
|
|
326
|
+
else:
|
|
327
|
+
extracted_results.append(res)
|
|
328
|
+
return extracted_results, unexpected_components
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _query_one_with_detail(
|
|
332
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
|
|
333
|
+
) -> ValidationResult | UnexpectedComponent:
|
|
334
|
+
detail_info = cast(DetailBaseInfo, base_info.detail)
|
|
335
|
+
match detail_info.source_constraint_component:
|
|
336
|
+
case SH.MinCountConstraintComponent:
|
|
337
|
+
if base_info.result_path in FILE_VALUE_PROPERTIES:
|
|
338
|
+
return _query_general_violation_info(
|
|
339
|
+
base_info.result_bn, base_info, results_and_onto, ViolationType.GENERIC
|
|
340
|
+
)
|
|
341
|
+
return _query_for_value_type_violation(base_info, results_and_onto, data_graph)
|
|
342
|
+
case SH.PatternConstraintComponent:
|
|
343
|
+
return _query_pattern_constraint_component_violation(detail_info.detail_bn, base_info, results_and_onto)
|
|
344
|
+
case SH.ClassConstraintComponent:
|
|
345
|
+
return _query_class_constraint_component_violation(base_info, results_and_onto, data_graph)
|
|
346
|
+
case SH.InConstraintComponent | DASH.SingleLineConstraintComponent:
|
|
347
|
+
detail = cast(DetailBaseInfo, base_info.detail)
|
|
348
|
+
return _query_general_violation_info(detail.detail_bn, base_info, results_and_onto, ViolationType.GENERIC)
|
|
349
|
+
case _:
|
|
350
|
+
return UnexpectedComponent(str(detail_info.source_constraint_component))
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _query_class_constraint_component_violation(
|
|
354
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
|
|
355
|
+
) -> ValidationResult | UnexpectedComponent:
|
|
356
|
+
detail_info = cast(DetailBaseInfo, base_info.detail)
|
|
357
|
+
detail_path = next(results_and_onto.objects(detail_info.detail_bn, SH.resultPath))
|
|
358
|
+
if detail_path == RDF.type:
|
|
359
|
+
return _query_for_value_type_violation(base_info, results_and_onto, data_graph)
|
|
360
|
+
return _query_for_link_value_target_violation(base_info, results_and_onto, data_graph)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _query_for_value_type_violation(
|
|
364
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
|
|
365
|
+
) -> ValidationResult:
|
|
366
|
+
detail_info = cast(DetailBaseInfo, base_info.detail)
|
|
367
|
+
msg = next(results_and_onto.objects(detail_info.detail_bn, SH.resultMessage))
|
|
368
|
+
val = next(results_and_onto.objects(base_info.result_bn, SH.value))
|
|
369
|
+
val_type = next(data_graph.objects(val, RDF.type))
|
|
370
|
+
return ValidationResult(
|
|
371
|
+
violation_type=ViolationType.VALUE_TYPE,
|
|
372
|
+
res_iri=base_info.focus_node_iri,
|
|
373
|
+
res_class=base_info.focus_node_type,
|
|
374
|
+
severity=base_info.severity,
|
|
375
|
+
property=base_info.result_path,
|
|
376
|
+
expected=msg,
|
|
377
|
+
input_type=val_type,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _query_pattern_constraint_component_violation(
|
|
382
|
+
bn_with_info: SubjectObjectTypeAlias, base_info: ValidationResultBaseInfo, results_and_onto: Graph
|
|
383
|
+
) -> ValidationResult:
|
|
384
|
+
val = next(results_and_onto.objects(bn_with_info, SH.value))
|
|
385
|
+
msg = next(results_and_onto.objects(bn_with_info, SH.resultMessage))
|
|
386
|
+
return ValidationResult(
|
|
387
|
+
violation_type=ViolationType.PATTERN,
|
|
388
|
+
res_iri=base_info.focus_node_iri,
|
|
389
|
+
res_class=base_info.focus_node_type,
|
|
390
|
+
severity=base_info.severity,
|
|
391
|
+
property=base_info.result_path,
|
|
392
|
+
expected=msg,
|
|
393
|
+
input_value=val,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _query_general_violation_info(
|
|
398
|
+
result_bn: SubjectObjectTypeAlias,
|
|
399
|
+
base_info: ValidationResultBaseInfo,
|
|
400
|
+
results_and_onto: Graph,
|
|
401
|
+
violation_type: ViolationType,
|
|
402
|
+
value: SubjectObjectTypeAlias | None = None,
|
|
403
|
+
) -> ValidationResult:
|
|
404
|
+
if not value:
|
|
405
|
+
val = next(results_and_onto.objects(result_bn, SH.value), None)
|
|
406
|
+
else:
|
|
407
|
+
val = value
|
|
408
|
+
msg = next(results_and_onto.objects(result_bn, SH.resultMessage))
|
|
409
|
+
return ValidationResult(
|
|
410
|
+
violation_type=violation_type,
|
|
411
|
+
res_iri=base_info.focus_node_iri,
|
|
412
|
+
res_class=base_info.focus_node_type,
|
|
413
|
+
severity=base_info.severity,
|
|
414
|
+
property=base_info.result_path,
|
|
415
|
+
message=msg,
|
|
416
|
+
input_value=val,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _query_general_violation_info_with_value_as_string(
|
|
421
|
+
result_bn: SubjectObjectTypeAlias,
|
|
422
|
+
base_info: ValidationResultBaseInfo,
|
|
423
|
+
results_and_onto: Graph,
|
|
424
|
+
data: Graph,
|
|
425
|
+
) -> ValidationResult:
|
|
426
|
+
value_iri = next(results_and_onto.objects(result_bn, SH.focusNode))
|
|
427
|
+
value = next(data.objects(value_iri, KNORA_API.valueAsString))
|
|
428
|
+
return _query_general_violation_info(result_bn, base_info, results_and_onto, ViolationType.GENERIC, value)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _query_for_link_value_target_violation(
|
|
432
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
|
|
433
|
+
) -> ValidationResult:
|
|
434
|
+
detail_info = cast(DetailBaseInfo, base_info.detail)
|
|
435
|
+
target_iri = next(results_and_onto.objects(detail_info.detail_bn, SH.value))
|
|
436
|
+
target_rdf_type = next(data_graph.objects(target_iri, RDF.type), None)
|
|
437
|
+
expected_type = next(results_and_onto.objects(detail_info.detail_bn, SH.resultMessage))
|
|
438
|
+
return ValidationResult(
|
|
439
|
+
violation_type=ViolationType.LINK_TARGET,
|
|
440
|
+
res_iri=base_info.focus_node_iri,
|
|
441
|
+
res_class=base_info.focus_node_type,
|
|
442
|
+
severity=base_info.severity,
|
|
443
|
+
property=base_info.result_path,
|
|
444
|
+
expected=expected_type,
|
|
445
|
+
input_value=target_iri,
|
|
446
|
+
input_type=target_rdf_type,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _query_for_min_cardinality_violation(
|
|
451
|
+
base_info: ValidationResultBaseInfo, msg: SubjectObjectTypeAlias
|
|
452
|
+
) -> ValidationResult:
|
|
453
|
+
if base_info.result_path in LEGAL_INFO_PROPS:
|
|
454
|
+
violation_type = ViolationType.GENERIC
|
|
455
|
+
else:
|
|
456
|
+
violation_type = ViolationType.MIN_CARD
|
|
457
|
+
return ValidationResult(
|
|
458
|
+
violation_type=violation_type,
|
|
459
|
+
res_iri=base_info.focus_node_iri,
|
|
460
|
+
res_class=base_info.focus_node_type,
|
|
461
|
+
severity=base_info.severity,
|
|
462
|
+
property=base_info.result_path,
|
|
463
|
+
expected=msg,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _query_for_unique_value_violation(
|
|
468
|
+
base_info: ValidationResultBaseInfo,
|
|
469
|
+
results_and_onto: Graph,
|
|
470
|
+
) -> ValidationResult:
|
|
471
|
+
val = next(results_and_onto.objects(base_info.result_bn, SH.value))
|
|
472
|
+
return ValidationResult(
|
|
473
|
+
violation_type=ViolationType.UNIQUE_VALUE,
|
|
474
|
+
res_iri=base_info.focus_node_iri,
|
|
475
|
+
res_class=base_info.focus_node_type,
|
|
476
|
+
severity=base_info.severity,
|
|
477
|
+
property=base_info.result_path,
|
|
478
|
+
input_value=val,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _query_for_coexists_with_violation(
|
|
483
|
+
base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph, message: SubjectObjectTypeAlias
|
|
484
|
+
) -> ValidationResult:
|
|
485
|
+
source_shapes = next(results_and_onto.objects(base_info.result_bn, SH.sourceShape))
|
|
486
|
+
if source_shapes == API_SHAPES.seqnum_PropShape:
|
|
487
|
+
violation_type = ViolationType.SEQNUM_IS_PART_OF
|
|
488
|
+
value = None
|
|
489
|
+
prop = None
|
|
490
|
+
else:
|
|
491
|
+
violation_type = ViolationType.GENERIC
|
|
492
|
+
value = _get_value_as_string(base_info.result_bn, results_and_onto, data)
|
|
493
|
+
prop = base_info.result_path
|
|
494
|
+
return ValidationResult(
|
|
495
|
+
violation_type=violation_type,
|
|
496
|
+
res_iri=base_info.focus_node_iri,
|
|
497
|
+
res_class=base_info.focus_node_type,
|
|
498
|
+
property=prop,
|
|
499
|
+
severity=base_info.severity,
|
|
500
|
+
message=message,
|
|
501
|
+
input_value=value,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _get_value_as_string(result_bn: SubjectObjectTypeAlias, results: Graph, data: Graph) -> SubjectObjectTypeAlias:
|
|
506
|
+
value_iri = next(results.objects(result_bn, SH.focusNode))
|
|
507
|
+
return next(data.objects(value_iri, KNORA_API.valueAsString))
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
3
|
+
from rdflib import SH
|
|
4
|
+
from rdflib import URIRef
|
|
5
|
+
|
|
6
|
+
from dsp_tools.commands.validate_data.constants import FILE_VALUE_PROPERTIES
|
|
7
|
+
from dsp_tools.commands.validate_data.constants import FILEVALUE_DETAIL_INFO
|
|
8
|
+
from dsp_tools.commands.validate_data.constants import LEGAL_INFO_PROPS
|
|
9
|
+
from dsp_tools.commands.validate_data.mappers import RESULT_TO_PROBLEM_MAPPER
|
|
10
|
+
from dsp_tools.commands.validate_data.models.input_problems import InputProblem
|
|
11
|
+
from dsp_tools.commands.validate_data.models.input_problems import ProblemType
|
|
12
|
+
from dsp_tools.commands.validate_data.models.input_problems import Severity
|
|
13
|
+
from dsp_tools.commands.validate_data.models.validation import ReformattedIRI
|
|
14
|
+
from dsp_tools.commands.validate_data.models.validation import ValidationResult
|
|
15
|
+
from dsp_tools.commands.validate_data.models.validation import ViolationType
|
|
16
|
+
from dsp_tools.commands.validate_data.utils import reformat_any_iri
|
|
17
|
+
from dsp_tools.commands.validate_data.utils import reformat_data_iri
|
|
18
|
+
from dsp_tools.commands.validate_data.utils import reformat_onto_iri
|
|
19
|
+
from dsp_tools.error.exceptions import BaseError
|
|
20
|
+
from dsp_tools.utils.rdf_constants import KNORA_API
|
|
21
|
+
from dsp_tools.utils.rdf_constants import SubjectObjectTypeAlias
|
|
22
|
+
|
|
23
|
+
SEVERITY_MAPPER: dict[SubjectObjectTypeAlias, Severity] = {
|
|
24
|
+
SH.Violation: Severity.VIOLATION,
|
|
25
|
+
SH.Warning: Severity.WARNING,
|
|
26
|
+
SH.Info: Severity.INFO,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def reformat_extracted_results(results: list[ValidationResult]) -> list[InputProblem]:
|
|
31
|
+
return [_reformat_one_validation_result(x) for x in results]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _reformat_one_validation_result(validation_result: ValidationResult) -> InputProblem:
|
|
35
|
+
match validation_result.violation_type:
|
|
36
|
+
case ViolationType.MIN_CARD:
|
|
37
|
+
return _reformat_min_card(validation_result)
|
|
38
|
+
case (
|
|
39
|
+
ViolationType.MAX_CARD
|
|
40
|
+
| ViolationType.NON_EXISTING_CARD
|
|
41
|
+
| ViolationType.PATTERN
|
|
42
|
+
| ViolationType.UNIQUE_VALUE
|
|
43
|
+
| ViolationType.VALUE_TYPE as violation
|
|
44
|
+
):
|
|
45
|
+
problem = RESULT_TO_PROBLEM_MAPPER[violation]
|
|
46
|
+
return _reformat_generic(result=validation_result, problem_type=problem)
|
|
47
|
+
case ViolationType.GENERIC:
|
|
48
|
+
prop_str = None
|
|
49
|
+
if validation_result.property in LEGAL_INFO_PROPS or validation_result.property in FILE_VALUE_PROPERTIES:
|
|
50
|
+
prop_str = "bitstream / iiif-uri"
|
|
51
|
+
return _reformat_generic(validation_result, ProblemType.GENERIC, prop_string=prop_str)
|
|
52
|
+
case ViolationType.FILE_VALUE_PROHIBITED | ViolationType.FILE_VALUE_MISSING as violation:
|
|
53
|
+
problem = RESULT_TO_PROBLEM_MAPPER[violation]
|
|
54
|
+
return _reformat_generic(result=validation_result, problem_type=problem, prop_string="bitstream / iiif-uri")
|
|
55
|
+
case ViolationType.SEQNUM_IS_PART_OF:
|
|
56
|
+
return _reformat_generic(
|
|
57
|
+
result=validation_result, problem_type=ProblemType.GENERIC, prop_string="seqnum or isPartOf"
|
|
58
|
+
)
|
|
59
|
+
case ViolationType.LINK_TARGET:
|
|
60
|
+
return _reformat_link_target_violation_result(validation_result)
|
|
61
|
+
case _:
|
|
62
|
+
raise BaseError(f"An unknown violation result was found: {validation_result.__class__.__name__}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _reformat_min_card(result: ValidationResult) -> InputProblem:
|
|
66
|
+
iris = _reformat_main_iris(result)
|
|
67
|
+
if file_prop_info := FILEVALUE_DETAIL_INFO.get(cast(URIRef, result.property)):
|
|
68
|
+
prop_str, file_extensions = file_prop_info
|
|
69
|
+
detail_msg = None
|
|
70
|
+
problem_type = ProblemType.FILE_VALUE_MISSING
|
|
71
|
+
expected: str | None = f"This resource requires a file with one of the following extensions: {file_extensions}"
|
|
72
|
+
else:
|
|
73
|
+
prop_str = iris.prop_name
|
|
74
|
+
detail_msg = _convert_rdflib_input_to_string(result.message)
|
|
75
|
+
problem_type = ProblemType.MIN_CARD
|
|
76
|
+
expected = _convert_rdflib_input_to_string(result.expected)
|
|
77
|
+
|
|
78
|
+
return InputProblem(
|
|
79
|
+
problem_type=problem_type,
|
|
80
|
+
res_id=iris.res_id,
|
|
81
|
+
res_type=iris.res_type,
|
|
82
|
+
prop_name=prop_str,
|
|
83
|
+
severity=SEVERITY_MAPPER[result.severity],
|
|
84
|
+
message=detail_msg,
|
|
85
|
+
input_value=_convert_rdflib_input_to_string(result.input_value),
|
|
86
|
+
input_type=_convert_rdflib_input_to_string(result.input_type),
|
|
87
|
+
expected=expected,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _reformat_generic(
|
|
92
|
+
result: ValidationResult, problem_type: ProblemType, prop_string: str | None = None
|
|
93
|
+
) -> InputProblem:
|
|
94
|
+
iris = _reformat_main_iris(result)
|
|
95
|
+
user_prop = iris.prop_name if not prop_string else prop_string
|
|
96
|
+
return InputProblem(
|
|
97
|
+
problem_type=problem_type,
|
|
98
|
+
res_id=iris.res_id,
|
|
99
|
+
res_type=iris.res_type,
|
|
100
|
+
prop_name=user_prop,
|
|
101
|
+
severity=SEVERITY_MAPPER[result.severity],
|
|
102
|
+
message=_convert_rdflib_input_to_string(result.message),
|
|
103
|
+
input_value=_convert_rdflib_input_to_string(result.input_value),
|
|
104
|
+
input_type=_convert_rdflib_input_to_string(result.input_type),
|
|
105
|
+
expected=_convert_rdflib_input_to_string(result.expected),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _reformat_link_target_violation_result(result: ValidationResult) -> InputProblem:
|
|
110
|
+
iris = _reformat_main_iris(result)
|
|
111
|
+
input_type = None
|
|
112
|
+
expected = None
|
|
113
|
+
problem_type = ProblemType.INEXISTENT_LINKED_RESOURCE
|
|
114
|
+
# If it is a stand-off link, we want to preserve the message
|
|
115
|
+
if result.property == KNORA_API.hasStandoffLinkTo:
|
|
116
|
+
msg = str(result.message)
|
|
117
|
+
else:
|
|
118
|
+
msg = None
|
|
119
|
+
|
|
120
|
+
if result.input_type:
|
|
121
|
+
problem_type = ProblemType.LINK_TARGET_TYPE_MISMATCH
|
|
122
|
+
input_type = reformat_onto_iri(str(result.input_type))
|
|
123
|
+
expected = reformat_onto_iri(str(result.expected))
|
|
124
|
+
|
|
125
|
+
return InputProblem(
|
|
126
|
+
problem_type=problem_type,
|
|
127
|
+
res_id=iris.res_id,
|
|
128
|
+
res_type=iris.res_type,
|
|
129
|
+
prop_name=iris.prop_name,
|
|
130
|
+
severity=SEVERITY_MAPPER[result.severity],
|
|
131
|
+
input_value=reformat_data_iri(str(result.input_value)),
|
|
132
|
+
input_type=input_type,
|
|
133
|
+
expected=expected,
|
|
134
|
+
message=msg,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _reformat_main_iris(result: ValidationResult) -> ReformattedIRI:
|
|
139
|
+
subject_id = reformat_data_iri(result.res_iri)
|
|
140
|
+
prop_name = reformat_onto_iri(result.property) if result.property else ""
|
|
141
|
+
res_type = reformat_onto_iri(result.res_class)
|
|
142
|
+
return ReformattedIRI(res_id=subject_id, res_type=res_type, prop_name=prop_name)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _convert_rdflib_input_to_string(input_val: SubjectObjectTypeAlias | None) -> str | None:
|
|
146
|
+
if not input_val:
|
|
147
|
+
return None
|
|
148
|
+
if isinstance(input_val, URIRef):
|
|
149
|
+
return reformat_any_iri(input_val)
|
|
150
|
+
return str(input_val)
|