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,99 @@
|
|
|
1
|
+
@prefix sh: <http://www.w3.org/ns/shacl#> .
|
|
2
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
3
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
4
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
5
|
+
@prefix knora-api: <http://api.knora.org/ontology/knora-api/v2#> .
|
|
6
|
+
|
|
7
|
+
@prefix api-shapes: <http://api.knora.org/ontology/knora-api/shapes/v2#> .
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
api-shapes:FindMissingMaxCardinality_Seqnum_OntologyShape
|
|
11
|
+
a sh:NodeShape ;
|
|
12
|
+
sh:targetClass owl:Class ;
|
|
13
|
+
sh:sparql [
|
|
14
|
+
a sh:SPARQLConstraint ;
|
|
15
|
+
sh:select """
|
|
16
|
+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
17
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
18
|
+
PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#>
|
|
19
|
+
|
|
20
|
+
SELECT $this ?path WHERE {
|
|
21
|
+
$this a owl:Class ;
|
|
22
|
+
rdfs:subClassOf ?restriction .
|
|
23
|
+
?restriction a owl:Restriction ;
|
|
24
|
+
owl:onProperty ?path .
|
|
25
|
+
|
|
26
|
+
?path rdfs:subPropertyOf* knora-api:seqnum .
|
|
27
|
+
|
|
28
|
+
FILTER NOT EXISTS {
|
|
29
|
+
?restriction owl:maxCardinality|owl:cardinality 1
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
""" ;
|
|
33
|
+
sh:message "seqnum must either have cardinality 1 or 0-1."
|
|
34
|
+
] ;
|
|
35
|
+
sh:severity sh:Violation .
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
api-shapes:FindMissingSeqnum_OntologyShape
|
|
39
|
+
a sh:NodeShape ;
|
|
40
|
+
sh:targetClass owl:Class ;
|
|
41
|
+
sh:sparql [
|
|
42
|
+
a sh:SPARQLConstraint ;
|
|
43
|
+
sh:select """
|
|
44
|
+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
45
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
46
|
+
PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#>
|
|
47
|
+
|
|
48
|
+
SELECT $this ?path WHERE {
|
|
49
|
+
|
|
50
|
+
$this a owl:Class ;
|
|
51
|
+
rdfs:subClassOf ?restrictionIsPartOf .
|
|
52
|
+
?restrictionIsPartOf a owl:Restriction ;
|
|
53
|
+
owl:onProperty ?path .
|
|
54
|
+
?path rdfs:subPropertyOf* knora-api:isPartOf .
|
|
55
|
+
|
|
56
|
+
FILTER NOT EXISTS {
|
|
57
|
+
$this a owl:Class ;
|
|
58
|
+
rdfs:subClassOf ?restrictionSeqnum .
|
|
59
|
+
?restrictionSeqnum a owl:Restriction ;
|
|
60
|
+
owl:onProperty ?seqnumProp .
|
|
61
|
+
?seqnumProp rdfs:subPropertyOf* knora-api:seqnum .
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
""" ;
|
|
65
|
+
sh:message "A class with a cardinality for isPartOf also requires a cardinality for seqnum."
|
|
66
|
+
] ;
|
|
67
|
+
sh:severity sh:Violation .
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
api-shapes:FindMissingIsPartOf_OntologyShape
|
|
71
|
+
a sh:NodeShape ;
|
|
72
|
+
sh:targetClass owl:Class ;
|
|
73
|
+
sh:sparql [
|
|
74
|
+
a sh:SPARQLConstraint ;
|
|
75
|
+
sh:select """
|
|
76
|
+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
77
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
78
|
+
PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#>
|
|
79
|
+
|
|
80
|
+
SELECT $this ?path WHERE {
|
|
81
|
+
|
|
82
|
+
$this a owl:Class ;
|
|
83
|
+
rdfs:subClassOf ?restrictionSeqnum .
|
|
84
|
+
?restrictionSeqnum a owl:Restriction ;
|
|
85
|
+
owl:onProperty ?path .
|
|
86
|
+
?path rdfs:subPropertyOf* knora-api:seqnum .
|
|
87
|
+
|
|
88
|
+
FILTER NOT EXISTS {
|
|
89
|
+
$this a owl:Class ;
|
|
90
|
+
rdfs:subClassOf ?restrictionIsPartOf .
|
|
91
|
+
?restrictionIsPartOf a owl:Restriction ;
|
|
92
|
+
owl:onProperty ?isPartOfProp .
|
|
93
|
+
?isPartOfProp rdfs:subPropertyOf* knora-api:isPartOf .
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
""" ;
|
|
97
|
+
sh:message "A class with a cardinality for seqnum also requires a cardinality for isPartOf."
|
|
98
|
+
] ;
|
|
99
|
+
sh:severity sh:Violation .
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# These are ANSI escape codes which can be used to configure the print output on the terminal
|
|
2
|
+
# http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
|
|
3
|
+
# The semicolon separates different configurations
|
|
4
|
+
|
|
5
|
+
# All codes must start with an escape sequence, it may differ in different languages, in Python "\u001b[" works
|
|
6
|
+
SEQUENCE_START = "\u001b["
|
|
7
|
+
|
|
8
|
+
# the "m" at the end signals,
|
|
9
|
+
# that the configuration code is finished and after that the string that should be printed starts
|
|
10
|
+
SEQUENCE_END = "m"
|
|
11
|
+
|
|
12
|
+
# reset to the default setting of the console
|
|
13
|
+
RESET_TO_DEFAULT = f"{SEQUENCE_START}0{SEQUENCE_END}"
|
|
14
|
+
|
|
15
|
+
BOLD = f"{SEQUENCE_START}1{SEQUENCE_END}" # 1 (bold) ; 32 (green)
|
|
16
|
+
|
|
17
|
+
# If you want to change for example both the text color and the background color you can combine the sequences
|
|
18
|
+
# for example: BACKGROUND_BOLD_MAGENTA + YELLOW -> magenta background with yellow text
|
|
19
|
+
|
|
20
|
+
# Colored Text
|
|
21
|
+
BOLD_GREEN = f"{SEQUENCE_START}1;32{SEQUENCE_END}" # 1 (bold) ; 32 (green)
|
|
22
|
+
BOLD_RED = f"{SEQUENCE_START}1;31{SEQUENCE_END}" # 1 (bold) ; 31 (red)
|
|
23
|
+
BOLD_CYAN = f"{SEQUENCE_START}1;36{SEQUENCE_END}" # 1 (bold) ; 36 (cyan)
|
|
24
|
+
BOLD_YELLOW = f"{SEQUENCE_START}1;33{SEQUENCE_END}" # 1 (bold) ; 33 (yellow)
|
|
25
|
+
YELLOW = f"{SEQUENCE_START}0;33{SEQUENCE_END}" # 0 (normal font) ; 33 (yellow)
|
|
26
|
+
RED = f"{SEQUENCE_START}0;31{SEQUENCE_END}" # 0 (normal font) ; 31 (red)
|
|
27
|
+
|
|
28
|
+
# Colored Background
|
|
29
|
+
BACKGROUND_BOLD_RED = f"{SEQUENCE_START}1;41{SEQUENCE_END}" # 1 (bold) ; 41 (background red)
|
|
30
|
+
BACKGROUND_BOLD_YELLOW = f"{SEQUENCE_START}1;43{SEQUENCE_END}" # 1 (bold) ; 43 (background yellow)
|
|
31
|
+
BACKGROUND_BOLD_GREEN = f"{SEQUENCE_START}1;42{SEQUENCE_END}" # 1 (bold) ; 42 (background green)
|
|
32
|
+
BACKGROUND_BOLD_CYAN = f"{SEQUENCE_START}1;46{SEQUENCE_END}" # 1 (bold) ; 46 (background cyan)
|
|
File without changes
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
import regex
|
|
8
|
+
|
|
9
|
+
from dsp_tools.error.exceptions import BaseError
|
|
10
|
+
|
|
11
|
+
_calendar = r"GREGORIAN|JULIAN|ISLAMIC"
|
|
12
|
+
_era = r"CE|BCE|BC|AD"
|
|
13
|
+
_year = r"\d{1,4}"
|
|
14
|
+
_month = r"\d{1,2}"
|
|
15
|
+
_day = r"\d{1,2}"
|
|
16
|
+
_full_date_pattern = rf"""
|
|
17
|
+
^
|
|
18
|
+
(?:({_calendar}):)? # optional calendar
|
|
19
|
+
(?:({_era}):)? # optional era
|
|
20
|
+
({_year}(?:-{_month})?(?:-{_day})?) # date
|
|
21
|
+
(?::({_era}))? # optional era
|
|
22
|
+
(?::({_year}(?:-{_month})?(?:-{_day})?))? # optional date
|
|
23
|
+
$
|
|
24
|
+
"""
|
|
25
|
+
_single_date_pattern = rf"^({_year})(?:-({_month}))?(?:-({_day}))?$"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Calendar(Enum):
|
|
29
|
+
"""Enum for calendar types."""
|
|
30
|
+
|
|
31
|
+
GREGORIAN = "GREGORIAN"
|
|
32
|
+
ISLAMIC = "ISLAMIC"
|
|
33
|
+
JULIAN = "JULIAN"
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def from_string(s: str) -> Calendar:
|
|
37
|
+
"""Parses a string into a calendar type, potentially failing with a BaseError."""
|
|
38
|
+
match s:
|
|
39
|
+
case "GREGORIAN":
|
|
40
|
+
return Calendar.GREGORIAN
|
|
41
|
+
case "ISLAMIC":
|
|
42
|
+
return Calendar.ISLAMIC
|
|
43
|
+
case "JULIAN":
|
|
44
|
+
return Calendar.JULIAN
|
|
45
|
+
case _:
|
|
46
|
+
raise BaseError(f"Invalid calendar type: {s}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Era(Enum):
|
|
50
|
+
"""Enum for era types."""
|
|
51
|
+
|
|
52
|
+
AD = "AD"
|
|
53
|
+
BC = "BC"
|
|
54
|
+
BCE = "BCE"
|
|
55
|
+
CE = "CE"
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def from_string(s: str) -> Era:
|
|
59
|
+
"""Parses a string into an era type, potentially failing with a BaseError."""
|
|
60
|
+
match s:
|
|
61
|
+
case "AD":
|
|
62
|
+
return Era.AD
|
|
63
|
+
case "BC":
|
|
64
|
+
return Era.BC
|
|
65
|
+
case "BCE":
|
|
66
|
+
return Era.BCE
|
|
67
|
+
case "CE":
|
|
68
|
+
return Era.CE
|
|
69
|
+
case _:
|
|
70
|
+
raise BaseError(f"Invalid era type: {s}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class DayMonthYearEra(Enum):
|
|
74
|
+
DAY = "Day"
|
|
75
|
+
MONTH = "Month"
|
|
76
|
+
YEAR = "Year"
|
|
77
|
+
ERA = "Era"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class StartEnd(Enum):
|
|
81
|
+
START = "Start"
|
|
82
|
+
END = "End"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True)
|
|
86
|
+
class SingleDate:
|
|
87
|
+
"""Information about a single date."""
|
|
88
|
+
|
|
89
|
+
era: Era | None
|
|
90
|
+
year: int
|
|
91
|
+
month: int | None
|
|
92
|
+
day: int | None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True)
|
|
96
|
+
class Date:
|
|
97
|
+
"""Information about a date."""
|
|
98
|
+
|
|
99
|
+
calendar: Calendar
|
|
100
|
+
start: SingleDate
|
|
101
|
+
end: SingleDate | None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def parse_date_string(s: str) -> Date:
|
|
105
|
+
"""Parse a date string into a Date object.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
s: date string
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Date object
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
BaseError: if the date string cannot be parsed
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
calendar, start_era, start_date, end_era, end_date = _split_date_string(s)
|
|
118
|
+
calendar_enum = Calendar.from_string(calendar or "GREGORIAN")
|
|
119
|
+
if not end_date:
|
|
120
|
+
end_date = start_date
|
|
121
|
+
if calendar_enum != Calendar.ISLAMIC:
|
|
122
|
+
if not start_era:
|
|
123
|
+
start_era = "CE"
|
|
124
|
+
if end_date and not end_era:
|
|
125
|
+
end_era = start_era
|
|
126
|
+
start_era_enum = Era.from_string(start_era) if start_era else None
|
|
127
|
+
end_era_enum = Era.from_string(end_era) if end_era else None
|
|
128
|
+
start = _parse_single_date(start_date, start_era_enum)
|
|
129
|
+
end = _parse_single_date(end_date, end_era_enum) if end_date else None
|
|
130
|
+
|
|
131
|
+
return Date(calendar_enum, start, end)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def is_full_date(s: str) -> bool:
|
|
135
|
+
"""
|
|
136
|
+
Check if a string is a full DSP date string of the scheme calendar:epoch:yyyy-mm-dd:epoch:yyyy-mm-dd.
|
|
137
|
+
"""
|
|
138
|
+
return bool(regex.search(_full_date_pattern, s, flags=regex.VERBOSE))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _split_date_string(s: str) -> tuple[str | None, str | None, str, str | None, str | None]:
|
|
142
|
+
date_match = regex.search(_full_date_pattern, s, flags=regex.VERBOSE)
|
|
143
|
+
if not date_match:
|
|
144
|
+
raise BaseError(f"Could not parse date: {s}")
|
|
145
|
+
date_groups = date_match.groups()
|
|
146
|
+
match date_groups:
|
|
147
|
+
case ("ISLAMIC", start_era, _, end_era, _) if start_era or end_era:
|
|
148
|
+
raise BaseError(f"ISLAMIC calendar does not support eras: {s}")
|
|
149
|
+
case (str() | None, str() | None, str(), str() | None, str() | None):
|
|
150
|
+
return cast(tuple[str | None, str | None, str, str | None, str | None], date_groups)
|
|
151
|
+
case _:
|
|
152
|
+
raise BaseError(f"Could not parse date: {s}")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _parse_single_date(date: str, era: Era | None) -> SingleDate:
|
|
156
|
+
date_match = regex.search(_single_date_pattern, date)
|
|
157
|
+
if not date_match:
|
|
158
|
+
raise BaseError(f"Could not parse date: {date}")
|
|
159
|
+
match date_match.groups():
|
|
160
|
+
case (str() as year, str() | None as month, str() | None as day):
|
|
161
|
+
y = int(year)
|
|
162
|
+
m = int(month) if month else None
|
|
163
|
+
d = int(day) if day else None
|
|
164
|
+
return SingleDate(era, y, m, d)
|
|
165
|
+
case _:
|
|
166
|
+
raise BaseError(f"Could not parse date: {date}")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import regex
|
|
2
|
+
|
|
3
|
+
_iri_pattern = r"^(http)s?://[\w\.\-~]*(:\d{,6})?(/[\w\-~]+)*(#[\w\-~]*)?"
|
|
4
|
+
_resource_iri_pattern = r"https?://rdfh.ch/[a-fA-F0-9]{4}/[\w-]{22}"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def is_iri(s: str) -> bool:
|
|
8
|
+
"""Checks whether a string is a valid IRI."""
|
|
9
|
+
return regex.fullmatch(_iri_pattern, s) is not None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_resource_iri(s: str) -> bool:
|
|
13
|
+
"""Checks whether a string is a valid resource IRI."""
|
|
14
|
+
return regex.fullmatch(_resource_iri_pattern, s) is not None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def from_dsp_iri_to_prefixed_iri(iri: str) -> str:
|
|
18
|
+
dsp_iri_re = r".+\/(.+?)\/v2#(.+)$"
|
|
19
|
+
if not (found := regex.search(dsp_iri_re, iri)):
|
|
20
|
+
return iri
|
|
21
|
+
return f"{found.group(1)}:{found.group(2)}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def make_dsp_ontology_prefix(api_url: str, shortcode: str, onto_name: str) -> str:
|
|
25
|
+
api_fixed = convert_api_url_for_correct_iri_namespace_construction(api_url)
|
|
26
|
+
return f"{api_fixed}/ontology/{shortcode}/{onto_name}/v2#"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def convert_api_url_for_correct_iri_namespace_construction(api_url: str) -> str:
|
|
30
|
+
return regex.sub(r"^https", "http", api_url)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import unicodedata
|
|
4
|
+
from typing import Any
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from typing import TypeGuard
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import regex
|
|
10
|
+
|
|
11
|
+
from dsp_tools.commands.excel2xml.propertyelement import PropertyElement
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def simplify_name(value: str) -> str:
|
|
15
|
+
"""
|
|
16
|
+
Simplifies a given value in order to use it as node name
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
value: The value to be simplified
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: The simplified value
|
|
23
|
+
"""
|
|
24
|
+
simplified_value = value.lower()
|
|
25
|
+
|
|
26
|
+
# normalize characters (p.ex. ä becomes a)
|
|
27
|
+
simplified_value = unicodedata.normalize("NFKD", simplified_value)
|
|
28
|
+
|
|
29
|
+
# replace forward slash and whitespace with a dash
|
|
30
|
+
simplified_value = regex.sub("[/\\s]+", "-", simplified_value)
|
|
31
|
+
|
|
32
|
+
# delete all characters which are not letters, numbers or dashes
|
|
33
|
+
simplified_value = regex.sub("[^A-Za-z0-9\\-]+", "", simplified_value)
|
|
34
|
+
|
|
35
|
+
return simplified_value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_notna(value: Optional[Any]) -> TypeGuard[Any]:
|
|
39
|
+
"""
|
|
40
|
+
Check a value if it is usable in the context of data archiving. A value is considered usable if it is
|
|
41
|
+
- a number (integer or float, but not np.nan)
|
|
42
|
+
- a boolean
|
|
43
|
+
- a string with at least one Unicode letter (matching the regex ``\\p{L}``) or number, or at least one _, !, or ?
|
|
44
|
+
(The strings `None`, `<NA>`, `N/A`, and `-` are considered invalid.)
|
|
45
|
+
- a PropertyElement whose "value" fulfills the above criteria
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
value: any object encountered when analysing data
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True if the value is usable, False if it is N/A or otherwise unusable
|
|
52
|
+
|
|
53
|
+
Examples:
|
|
54
|
+
>>> check_notna(0) == True
|
|
55
|
+
>>> check_notna(False) == True
|
|
56
|
+
>>> check_notna("œ") == True
|
|
57
|
+
>>> check_notna("0") == True
|
|
58
|
+
>>> check_notna("_") == True
|
|
59
|
+
>>> check_notna("!") == True
|
|
60
|
+
>>> check_notna("?") == True
|
|
61
|
+
>>> check_notna(None) == False
|
|
62
|
+
>>> check_notna("None") == False
|
|
63
|
+
>>> check_notna(<NA>) == False
|
|
64
|
+
>>> check_notna("<NA>") == False
|
|
65
|
+
>>> check_notna("-") == False
|
|
66
|
+
>>> check_notna(" ") == False
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
if isinstance(value, PropertyElement):
|
|
70
|
+
value = value.value
|
|
71
|
+
|
|
72
|
+
if isinstance(value, bool | int) or (
|
|
73
|
+
isinstance(value, float) and pd.notna(value)
|
|
74
|
+
): # necessary because isinstance(np.nan, float)
|
|
75
|
+
return True
|
|
76
|
+
elif isinstance(value, str):
|
|
77
|
+
return bool(regex.search(r"[\p{L}\d_!?]", value, flags=regex.UNICODE)) and not bool(
|
|
78
|
+
regex.search(r"^(none|<NA>|-|n/a)$", value, flags=regex.IGNORECASE)
|
|
79
|
+
)
|
|
80
|
+
else:
|
|
81
|
+
return False
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import regex
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_prod_like_server(server: str) -> bool:
|
|
5
|
+
return server in [
|
|
6
|
+
"https://api.dasch.swiss",
|
|
7
|
+
"https://api.rdu.dasch.swiss",
|
|
8
|
+
"https://api.ls-prod-server.dasch.swiss",
|
|
9
|
+
"https://api.ls-test-server.dasch.swiss",
|
|
10
|
+
"https://api.stage.dasch.swiss",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_uri(s: str) -> bool:
|
|
15
|
+
"""Checks if the given string is a valid URI."""
|
|
16
|
+
# URI = scheme ":" ["//" host [":" port]] path ["?" query] ["#" fragment]
|
|
17
|
+
chars_for_path_query_fragment = r"\w_.\-:~%()!@,;/=*&'+"
|
|
18
|
+
scheme = r"(?<scheme>[a-z][a-z0-9+.\-]*)"
|
|
19
|
+
host = r"(?<host>[\w_.\-:~\[\]]+)"
|
|
20
|
+
port = r"(?<port>:\d{0,6})"
|
|
21
|
+
path = rf"(?<path>/[{chars_for_path_query_fragment}]*)"
|
|
22
|
+
query = rf"(?<query>\?[{chars_for_path_query_fragment}]+)"
|
|
23
|
+
fragment = rf"(?<fragment>#[{chars_for_path_query_fragment}]*)"
|
|
24
|
+
m = regex.match(rf"{scheme}:(//{host}{port}?){path}*{query}*{fragment}?", s, flags=regex.UNICODE)
|
|
25
|
+
return m is not None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_iiif_uri(uri: str) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Checks if the given URL is a valid IIIF URL.
|
|
31
|
+
It should support all versions of IIIF servers,
|
|
32
|
+
but was constructed with the syntax described in: https://iiif.io/api/image/3.0/#2-uri-syntax.
|
|
33
|
+
IIIF is open to modifications which are not described in the official documentation
|
|
34
|
+
(see https://iiif.io/api/annex/notes/design_principles/#define-success-not-failure).
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
uri: The URI to be checked.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
True if the URI is a valid IIIF URI, False otherwise.
|
|
41
|
+
"""
|
|
42
|
+
# {scheme}://{server}/{prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
|
|
43
|
+
split_uri = uri.lower().split("/")
|
|
44
|
+
try:
|
|
45
|
+
region_seg, size_seg, rotation_seg, quality_format_seg = split_uri[-4:]
|
|
46
|
+
except ValueError:
|
|
47
|
+
return False
|
|
48
|
+
# %5E is the URL encoded version of ^ ->
|
|
49
|
+
# because we do change the uri to lower case we need to change that in the regex
|
|
50
|
+
# (\d+(\.\d+)?) -> number, can be integer or float
|
|
51
|
+
# everything needs to be encapsulated in a group
|
|
52
|
+
# otherwise the ^ and $ only apply respectively to the first and last listed in the alternative options
|
|
53
|
+
region_re = (
|
|
54
|
+
r"^(full|square|" # full | square
|
|
55
|
+
r"((pct:)?(\d+(\.\d+)?,){3}(\d+(\.\d+)?)))$" # x,y,w,h | pct:x,y,w,h
|
|
56
|
+
)
|
|
57
|
+
if not regex.search(region_re, region_seg):
|
|
58
|
+
return False
|
|
59
|
+
size_re = (
|
|
60
|
+
r"^((\^|%5e)?max|" # max | ^max
|
|
61
|
+
r"(\^|%5e)?full|" # full | ^full
|
|
62
|
+
r"(\^|%5e)?pct:\d+(\.\d+)?|" # pct:n | ^pct:n
|
|
63
|
+
r"(\^|%5e)?(\d+(\.\d+)?)+(,|%2c)|" # w, | ^w,
|
|
64
|
+
r"(\^|%5e)?,\d+(\.\d+)?|" # ,h | ^,h
|
|
65
|
+
r"(\^|%5e)?!?\d+(\.\d+)?,\d+(\.\d+)?)$" # w,h | ^w,h | !w,h | ^!w,h
|
|
66
|
+
)
|
|
67
|
+
if not regex.search(size_re, size_seg):
|
|
68
|
+
return False
|
|
69
|
+
# rotation -> floating point number 0-360 -> n | !n (positive and negative are allowed)
|
|
70
|
+
rotation_re = r"^!?[+-]?\d+(\.\d+)?$"
|
|
71
|
+
if not regex.search(rotation_re, rotation_seg):
|
|
72
|
+
return False
|
|
73
|
+
# quality -> color | colour | gray | grey | bitonal | default | native
|
|
74
|
+
# format -> jpg | tif | png | gif | jp2 | pdf | webp
|
|
75
|
+
quality_format_re = r"^(colou?r|gr[ae]y|bitonal|default|native)(\.(jpg|tif|png|jp2|gif|pdf|webp))?$"
|
|
76
|
+
return bool(regex.search(quality_format_re, quality_format_seg))
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import assert_never
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from dsp_tools.clients.fuseki_metrics import FusekiBloatingLevel
|
|
6
|
+
from dsp_tools.clients.fuseki_metrics import FusekiMetrics
|
|
7
|
+
from dsp_tools.config.logger_config import LOGGER_SAVEPATH
|
|
8
|
+
from dsp_tools.utils.ansi_colors import BACKGROUND_BOLD_RED
|
|
9
|
+
from dsp_tools.utils.ansi_colors import BOLD_YELLOW
|
|
10
|
+
from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
|
|
11
|
+
|
|
12
|
+
_1_GB_IN_BYTES = 1_000_000_000
|
|
13
|
+
WARNING_BLOATING = 10 * _1_GB_IN_BYTES
|
|
14
|
+
CRITICAL_BLOATING = 20 * _1_GB_IN_BYTES
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def communicate_fuseki_bloating(fuseki_metrics: FusekiMetrics) -> None:
|
|
18
|
+
size_diff = _analyse_fuseki_sizes(fuseki_metrics)
|
|
19
|
+
bloating_level = _get_bloating_level(size_diff)
|
|
20
|
+
if size_diff is not None:
|
|
21
|
+
rounded = round(size_diff / _1_GB_IN_BYTES, 2)
|
|
22
|
+
else:
|
|
23
|
+
rounded = None
|
|
24
|
+
msg = (
|
|
25
|
+
f"The xmlupload caused the database to use {rounded} GB disk space. "
|
|
26
|
+
f"Please check that your test server has enough disk space for an upload. "
|
|
27
|
+
f"If you have any questions contact the dsp-tools developers at support@dasch.swiss."
|
|
28
|
+
)
|
|
29
|
+
match bloating_level:
|
|
30
|
+
case FusekiBloatingLevel.OK:
|
|
31
|
+
logger.debug(msg)
|
|
32
|
+
case FusekiBloatingLevel.WARNING:
|
|
33
|
+
logger.warning(msg)
|
|
34
|
+
print(f"{BOLD_YELLOW}WARNING: {msg}{RESET_TO_DEFAULT}")
|
|
35
|
+
case FusekiBloatingLevel.CRITICAL:
|
|
36
|
+
logger.warning(msg)
|
|
37
|
+
print(f"{BACKGROUND_BOLD_RED}WARNING: {msg}{RESET_TO_DEFAULT}")
|
|
38
|
+
case FusekiBloatingLevel.CALCULATION_FAILURE:
|
|
39
|
+
msg = (
|
|
40
|
+
"The database bloating size could not be calculated. "
|
|
41
|
+
f"Please contact the dsp-tools developers (at support@dasch.swiss) "
|
|
42
|
+
f"with your logs saved at {LOGGER_SAVEPATH}."
|
|
43
|
+
)
|
|
44
|
+
print(f"{BACKGROUND_BOLD_RED}{msg}{RESET_TO_DEFAULT}")
|
|
45
|
+
logger.error(msg)
|
|
46
|
+
case _:
|
|
47
|
+
assert_never(bloating_level)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _get_bloating_level(size_diff: int | None) -> FusekiBloatingLevel:
|
|
51
|
+
if size_diff is None:
|
|
52
|
+
return FusekiBloatingLevel.CALCULATION_FAILURE
|
|
53
|
+
if size_diff <= WARNING_BLOATING:
|
|
54
|
+
return FusekiBloatingLevel.OK
|
|
55
|
+
if size_diff <= CRITICAL_BLOATING:
|
|
56
|
+
return FusekiBloatingLevel.WARNING
|
|
57
|
+
return FusekiBloatingLevel.CRITICAL
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _analyse_fuseki_sizes(fuseki_metrics: FusekiMetrics) -> int | None:
|
|
61
|
+
if fuseki_metrics.end_size is not None and fuseki_metrics.start_size is not None:
|
|
62
|
+
return fuseki_metrics.end_size - fuseki_metrics.start_size
|
|
63
|
+
return None
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing import cast
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
from dsp_tools.error.exceptions import JSONFileParsingError
|
|
9
|
+
from dsp_tools.error.exceptions import UserFilepathNotFoundError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_json_file(filepath: Path) -> dict[str, Any]:
|
|
13
|
+
if not filepath.exists():
|
|
14
|
+
raise UserFilepathNotFoundError(filepath)
|
|
15
|
+
with open(filepath, encoding="utf-8") as f:
|
|
16
|
+
try:
|
|
17
|
+
loaded = json.load(f)
|
|
18
|
+
return cast(dict[str, Any], loaded)
|
|
19
|
+
except json.JSONDecodeError as e:
|
|
20
|
+
logger.error(e)
|
|
21
|
+
msg = f"The input file '{filepath}' cannot be parsed to a JSON object."
|
|
22
|
+
raise JSONFileParsingError(msg) from None
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from rdflib import IdentifiedNode
|
|
4
|
+
from rdflib import Literal
|
|
5
|
+
from rdflib import Namespace
|
|
6
|
+
from rdflib import Variable
|
|
7
|
+
from rdflib.term import Node
|
|
8
|
+
|
|
9
|
+
# naming conventions of prefixes and namespaces
|
|
10
|
+
|
|
11
|
+
# a prefix as a string is called [...]_PREFIX
|
|
12
|
+
# a prefix as a rdflib Namespace has no further suffix
|
|
13
|
+
|
|
14
|
+
#####
|
|
15
|
+
# rdflib typing
|
|
16
|
+
type PropertyTypeAlias = Union[IdentifiedNode, Variable]
|
|
17
|
+
type SubjectObjectTypeAlias = Union[IdentifiedNode, Literal, Variable, Node]
|
|
18
|
+
|
|
19
|
+
#####
|
|
20
|
+
# DSP-Namespaces
|
|
21
|
+
KNORA_API_PREFIX = "http://api.knora.org/ontology/knora-api/v2#"
|
|
22
|
+
KNORA_API = Namespace(KNORA_API_PREFIX)
|
|
23
|
+
|
|
24
|
+
KNORA_ADMIN_PREFIX = "http://www.knora.org/ontology/knora-admin#"
|
|
25
|
+
|
|
26
|
+
SALSAH_GUI_PREFIX = "http://api.knora.org/ontology/salsah-gui/v2#"
|
|
27
|
+
SALSAH_GUI = Namespace(SALSAH_GUI_PREFIX)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
#####
|
|
31
|
+
# Mappers
|
|
32
|
+
|
|
33
|
+
DSP_NAME_TO_PREFIX = {"knora-api": KNORA_API_PREFIX, "salsah-gui": SALSAH_GUI_PREFIX}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
#####
|
|
37
|
+
# For validate-data
|
|
38
|
+
API_SHAPES_PREFIX = "http://api.knora.org/ontology/knora-api/shapes/v2#"
|
|
39
|
+
API_SHAPES = Namespace(API_SHAPES_PREFIX)
|
|
40
|
+
DATA = Namespace("http://data/")
|
|
41
|
+
DASH_PREFIX = "http://datashapes.org/dash#"
|
|
42
|
+
DASH = Namespace(DASH_PREFIX)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from rdflib import Graph
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def serialise_json(rdf_graph: Graph) -> list[dict[str, Any]]:
|
|
8
|
+
graph_bytes = rdf_graph.serialize(format="json-ld", encoding="utf-8")
|
|
9
|
+
json_graph: list[dict[str, Any]] = json.loads(graph_bytes)
|
|
10
|
+
return json_graph
|