dsp-tools 9.1.0.post11__py3-none-any.whl → 18.3.0.post13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsp_tools/__init__.py +4 -0
- dsp_tools/cli/args.py +36 -0
- dsp_tools/cli/call_action.py +51 -231
- dsp_tools/cli/call_action_files_only.py +101 -0
- dsp_tools/cli/call_action_with_network.py +207 -0
- dsp_tools/cli/create_parsers.py +156 -58
- dsp_tools/cli/entry_point.py +56 -26
- dsp_tools/cli/utils.py +87 -0
- dsp_tools/clients/CLAUDE.md +420 -0
- dsp_tools/clients/authentication_client.py +14 -0
- dsp_tools/clients/authentication_client_live.py +66 -0
- dsp_tools/{utils → clients}/connection.py +2 -18
- dsp_tools/clients/connection_live.py +233 -0
- dsp_tools/clients/fuseki_metrics.py +60 -0
- dsp_tools/clients/group_user_clients.py +35 -0
- dsp_tools/clients/group_user_clients_live.py +181 -0
- dsp_tools/clients/legal_info_client.py +23 -0
- dsp_tools/clients/legal_info_client_live.py +132 -0
- dsp_tools/clients/list_client.py +49 -0
- dsp_tools/clients/list_client_live.py +166 -0
- dsp_tools/clients/metadata_client.py +24 -0
- dsp_tools/clients/metadata_client_live.py +47 -0
- dsp_tools/clients/ontology_clients.py +49 -0
- dsp_tools/clients/ontology_create_client_live.py +166 -0
- dsp_tools/clients/ontology_get_client_live.py +80 -0
- dsp_tools/clients/permissions_client.py +68 -0
- dsp_tools/clients/project_client.py +16 -0
- dsp_tools/clients/project_client_live.py +66 -0
- dsp_tools/commands/create/communicate_problems.py +24 -0
- dsp_tools/commands/create/create.py +134 -0
- dsp_tools/commands/create/create_on_server/cardinalities.py +111 -0
- dsp_tools/commands/create/create_on_server/classes.py +99 -0
- dsp_tools/commands/create/create_on_server/complete_ontologies.py +116 -0
- dsp_tools/commands/create/create_on_server/default_permissions.py +134 -0
- dsp_tools/commands/create/create_on_server/group_users.py +165 -0
- dsp_tools/commands/create/create_on_server/lists.py +163 -0
- dsp_tools/commands/create/create_on_server/mappers.py +12 -0
- dsp_tools/commands/create/create_on_server/onto_utils.py +74 -0
- dsp_tools/commands/create/create_on_server/ontology.py +52 -0
- dsp_tools/commands/create/create_on_server/project.py +68 -0
- dsp_tools/commands/create/create_on_server/properties.py +119 -0
- dsp_tools/commands/create/exceptions.py +29 -0
- dsp_tools/commands/create/lists_only.py +66 -0
- dsp_tools/commands/create/models/create_problems.py +87 -0
- dsp_tools/commands/create/models/parsed_ontology.py +88 -0
- dsp_tools/commands/create/models/parsed_project.py +81 -0
- dsp_tools/commands/create/models/rdf_ontology.py +12 -0
- dsp_tools/commands/create/models/server_project_info.py +100 -0
- dsp_tools/commands/create/parsing/parse_lists.py +45 -0
- dsp_tools/commands/create/parsing/parse_ontology.py +243 -0
- dsp_tools/commands/create/parsing/parse_project.py +149 -0
- dsp_tools/commands/create/parsing/parsing_utils.py +40 -0
- dsp_tools/commands/create/project_validate.py +595 -0
- dsp_tools/commands/create/serialisation/ontology.py +119 -0
- dsp_tools/commands/create/serialisation/project.py +44 -0
- dsp_tools/commands/excel2json/CLAUDE.md +101 -0
- dsp_tools/commands/excel2json/json_header.py +57 -23
- dsp_tools/commands/excel2json/{new_lists → lists}/compliance_checks.py +26 -26
- dsp_tools/commands/excel2json/{new_lists/make_new_lists.py → lists/make_lists.py} +19 -18
- dsp_tools/commands/excel2json/{new_lists → lists}/models/input_error.py +1 -12
- dsp_tools/commands/excel2json/{new_lists → lists}/models/serialise.py +9 -5
- dsp_tools/commands/excel2json/{new_lists → lists}/utils.py +4 -4
- dsp_tools/commands/excel2json/models/input_error.py +31 -11
- dsp_tools/commands/excel2json/models/json_header.py +53 -15
- dsp_tools/commands/excel2json/models/ontology.py +4 -3
- dsp_tools/commands/excel2json/{lists.py → old_lists.py} +26 -112
- dsp_tools/commands/excel2json/project.py +78 -34
- dsp_tools/commands/excel2json/properties.py +57 -36
- dsp_tools/commands/excel2json/resources.py +32 -12
- dsp_tools/commands/excel2json/utils.py +20 -1
- dsp_tools/commands/excel2xml/__init__.py +2 -2
- dsp_tools/commands/excel2xml/excel2xml_cli.py +7 -15
- dsp_tools/commands/excel2xml/excel2xml_lib.py +138 -493
- dsp_tools/commands/excel2xml/propertyelement.py +5 -5
- dsp_tools/commands/{project → get}/get.py +29 -13
- dsp_tools/commands/get/get_permissions.py +257 -0
- dsp_tools/commands/get/get_permissions_legacy.py +89 -0
- dsp_tools/commands/{project/models → get/legacy_models}/context.py +6 -6
- dsp_tools/commands/{project/models → get/legacy_models}/group.py +5 -10
- dsp_tools/commands/{project/models → get/legacy_models}/listnode.py +5 -35
- dsp_tools/commands/{project/models → get/legacy_models}/model.py +1 -1
- dsp_tools/commands/{project/models → get/legacy_models}/ontology.py +9 -14
- dsp_tools/commands/{project/models → get/legacy_models}/project.py +13 -6
- dsp_tools/commands/{project/models → get/legacy_models}/propertyclass.py +9 -16
- dsp_tools/commands/{project/models → get/legacy_models}/resourceclass.py +8 -46
- dsp_tools/commands/{project/models → get/legacy_models}/user.py +19 -60
- dsp_tools/commands/get/models/permissions_models.py +10 -0
- dsp_tools/commands/id2iri.py +20 -10
- dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +81 -56
- dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +4 -10
- dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +97 -37
- dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +2 -2
- dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +9 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +3 -3
- dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +2 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +12 -2
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +8 -9
- dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +18 -18
- dsp_tools/commands/start_stack.py +126 -77
- dsp_tools/commands/update_legal/CLAUDE.md +344 -0
- dsp_tools/commands/update_legal/__init__.py +0 -0
- dsp_tools/commands/update_legal/core.py +182 -0
- dsp_tools/commands/update_legal/csv_operations.py +135 -0
- dsp_tools/commands/update_legal/models.py +87 -0
- dsp_tools/commands/update_legal/xml_operations.py +247 -0
- dsp_tools/commands/validate_data/CLAUDE.md +159 -0
- dsp_tools/commands/validate_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/constants.py +59 -0
- dsp_tools/commands/validate_data/mappers.py +143 -0
- dsp_tools/commands/validate_data/models/__init__.py +0 -0
- dsp_tools/commands/validate_data/models/api_responses.py +45 -0
- dsp_tools/commands/validate_data/models/input_problems.py +119 -0
- dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
- dsp_tools/commands/validate_data/models/validation.py +106 -0
- dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
- dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
- dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
- dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
- dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
- dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
- dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
- dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
- dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
- dsp_tools/commands/{xml_validate/sparql/resource_shacl.py → validate_data/sparql/cardinality_shacl.py} +45 -47
- dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
- dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
- dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
- dsp_tools/commands/validate_data/utils.py +59 -0
- dsp_tools/commands/validate_data/validate_data.py +283 -0
- dsp_tools/commands/validate_data/validation/__init__.py +0 -0
- dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
- dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
- dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
- dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
- dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
- dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
- dsp_tools/commands/xmlupload/models/formatted_text_value.py +0 -25
- dsp_tools/commands/xmlupload/models/ingest.py +56 -70
- dsp_tools/commands/xmlupload/models/input_problems.py +6 -14
- dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
- dsp_tools/commands/xmlupload/models/permission.py +0 -39
- dsp_tools/commands/xmlupload/models/{deserialise/xmlpermission.py → permissions_parsed.py} +2 -2
- dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
- dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
- dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
- dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
- dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
- dsp_tools/commands/xmlupload/models/upload_clients.py +3 -3
- dsp_tools/commands/xmlupload/models/upload_state.py +2 -4
- dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
- dsp_tools/commands/xmlupload/{ark2iri.py → prepare_xml_input/ark2iri.py} +1 -1
- dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
- dsp_tools/commands/xmlupload/{iiif_uri_validator.py → prepare_xml_input/iiif_uri_validator.py} +2 -14
- dsp_tools/commands/xmlupload/{list_client.py → prepare_xml_input/list_client.py} +15 -10
- dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
- dsp_tools/commands/xmlupload/resource_create_client.py +7 -468
- dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
- dsp_tools/commands/xmlupload/stash/{construct_and_analyze_graph.py → analyse_circular_reference_graph.py} +64 -157
- dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
- dsp_tools/commands/xmlupload/stash/graph_models.py +13 -8
- dsp_tools/commands/xmlupload/stash/stash_circular_references.py +48 -115
- dsp_tools/commands/xmlupload/stash/stash_models.py +4 -9
- dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +34 -40
- dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +98 -108
- dsp_tools/commands/xmlupload/upload_config.py +8 -0
- dsp_tools/commands/xmlupload/write_diagnostic_info.py +14 -9
- dsp_tools/commands/xmlupload/xmlupload.py +214 -192
- dsp_tools/config/__init__.py +0 -0
- dsp_tools/config/logger_config.py +69 -0
- dsp_tools/{utils → config}/warnings_config.py +4 -1
- dsp_tools/error/__init__.py +0 -0
- dsp_tools/error/custom_warnings.py +39 -0
- dsp_tools/error/exceptions.py +204 -0
- dsp_tools/error/problems.py +10 -0
- dsp_tools/error/xmllib_errors.py +20 -0
- dsp_tools/error/xmllib_warnings.py +54 -0
- dsp_tools/error/xmllib_warnings_util.py +159 -0
- dsp_tools/error/xsd_validation_error_msg.py +19 -0
- dsp_tools/legacy_models/__init__.py +0 -0
- dsp_tools/{models → legacy_models}/datetimestamp.py +7 -7
- dsp_tools/{models → legacy_models}/langstring.py +1 -1
- dsp_tools/{models → legacy_models}/projectContext.py +4 -4
- dsp_tools/resources/schema/data.xsd +108 -83
- dsp_tools/resources/schema/lists-only.json +4 -23
- dsp_tools/resources/schema/project.json +80 -35
- dsp_tools/resources/schema/properties-only.json +1 -4
- dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
- dsp_tools/resources/start-stack/docker-compose.yml +34 -30
- dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
- dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
- dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
- dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
- dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
- dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
- dsp_tools/utils/ansi_colors.py +32 -0
- dsp_tools/utils/data_formats/__init__.py +0 -0
- dsp_tools/utils/{date_util.py → data_formats/date_util.py} +13 -1
- dsp_tools/utils/data_formats/iri_util.py +30 -0
- dsp_tools/utils/{shared.py → data_formats/shared.py} +1 -35
- dsp_tools/utils/{uri_util.py → data_formats/uri_util.py} +12 -2
- dsp_tools/utils/fuseki_bloating.py +63 -0
- dsp_tools/utils/json_parsing.py +22 -0
- dsp_tools/utils/rdf_constants.py +42 -0
- dsp_tools/utils/rdflib_utils.py +10 -0
- dsp_tools/utils/replace_id_with_iri.py +66 -0
- dsp_tools/utils/request_utils.py +238 -0
- dsp_tools/utils/xml_parsing/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
- dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
- dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
- dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
- dsp_tools/xmllib/CLAUDE.md +302 -0
- dsp_tools/xmllib/__init__.py +49 -0
- dsp_tools/xmllib/general_functions.py +877 -0
- dsp_tools/xmllib/internal/__init__.py +0 -0
- dsp_tools/xmllib/internal/checkers.py +162 -0
- dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
- dsp_tools/xmllib/internal/constants.py +46 -0
- dsp_tools/xmllib/internal/input_converters.py +155 -0
- dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
- dsp_tools/xmllib/internal/serialise_resource.py +177 -0
- dsp_tools/xmllib/internal/serialise_values.py +152 -0
- dsp_tools/xmllib/internal/type_aliases.py +11 -0
- dsp_tools/xmllib/models/config_options.py +28 -0
- dsp_tools/xmllib/models/date_formats.py +48 -0
- dsp_tools/xmllib/models/dsp_base_resources.py +1380 -400
- dsp_tools/xmllib/models/internal/__init__.py +0 -0
- dsp_tools/xmllib/models/internal/file_values.py +172 -0
- dsp_tools/xmllib/models/internal/geometry.py +162 -0
- dsp_tools/xmllib/models/{migration_metadata.py → internal/migration_metadata.py} +14 -10
- dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
- dsp_tools/xmllib/models/internal/values.py +342 -0
- dsp_tools/xmllib/models/licenses/__init__.py +0 -0
- dsp_tools/xmllib/models/licenses/other.py +59 -0
- dsp_tools/xmllib/models/licenses/recommended.py +107 -0
- dsp_tools/xmllib/models/permissions.py +41 -0
- dsp_tools/xmllib/models/res.py +1782 -0
- dsp_tools/xmllib/models/root.py +313 -26
- dsp_tools/xmllib/value_checkers.py +310 -47
- dsp_tools/xmllib/value_converters.py +765 -8
- dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
- dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
- dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
- {dsp_tools-9.1.0.post11.dist-info → dsp_tools-18.3.0.post13.dist-info}/entry_points.txt +1 -0
- dsp_tools/commands/project/create/project_create.py +0 -1107
- dsp_tools/commands/project/create/project_create_lists.py +0 -204
- dsp_tools/commands/project/create/project_validate.py +0 -453
- dsp_tools/commands/project/models/project_definition.py +0 -12
- dsp_tools/commands/rosetta.py +0 -124
- dsp_tools/commands/template.py +0 -30
- dsp_tools/commands/xml_validate/api_connection.py +0 -122
- dsp_tools/commands/xml_validate/deserialise_input.py +0 -135
- dsp_tools/commands/xml_validate/make_data_rdf.py +0 -193
- dsp_tools/commands/xml_validate/models/data_deserialised.py +0 -108
- dsp_tools/commands/xml_validate/models/data_rdf.py +0 -214
- dsp_tools/commands/xml_validate/models/input_problems.py +0 -191
- dsp_tools/commands/xml_validate/models/validation.py +0 -29
- dsp_tools/commands/xml_validate/reformat_validaton_result.py +0 -89
- dsp_tools/commands/xml_validate/sparql/construct_shapes.py +0 -16
- dsp_tools/commands/xml_validate/xml_validate.py +0 -151
- dsp_tools/commands/xmlupload/check_consistency_with_ontology.py +0 -253
- dsp_tools/commands/xmlupload/models/deserialise/deserialise_value.py +0 -236
- dsp_tools/commands/xmlupload/models/deserialise/xmlresource.py +0 -171
- dsp_tools/commands/xmlupload/models/namespace_context.py +0 -39
- dsp_tools/commands/xmlupload/models/ontology_lookup_models.py +0 -161
- dsp_tools/commands/xmlupload/models/ontology_problem_models.py +0 -178
- dsp_tools/commands/xmlupload/models/serialise/jsonld_serialiser.py +0 -40
- dsp_tools/commands/xmlupload/models/serialise/serialise_value.py +0 -51
- dsp_tools/commands/xmlupload/ontology_client.py +0 -92
- dsp_tools/commands/xmlupload/project_client.py +0 -91
- dsp_tools/commands/xmlupload/read_validate_xml_file.py +0 -99
- dsp_tools/models/custom_warnings.py +0 -31
- dsp_tools/models/exceptions.py +0 -90
- dsp_tools/resources/0100-template-repo/template.json +0 -45
- dsp_tools/resources/0100-template-repo/template.xml +0 -27
- dsp_tools/resources/start-stack/docker-compose-validation.yml +0 -5
- dsp_tools/resources/start-stack/start-stack-config.yml +0 -4
- dsp_tools/resources/xml_validate/api-shapes.ttl +0 -411
- dsp_tools/resources/xml_validate/replace_namespace.xslt +0 -61
- dsp_tools/utils/connection_live.py +0 -383
- dsp_tools/utils/iri_util.py +0 -14
- dsp_tools/utils/logger_config.py +0 -41
- dsp_tools/utils/set_encoder.py +0 -20
- dsp_tools/utils/xml_utils.py +0 -145
- dsp_tools/utils/xml_validation.py +0 -197
- dsp_tools/utils/xml_validation_models.py +0 -68
- dsp_tools/xmllib/models/file_values.py +0 -78
- dsp_tools/xmllib/models/resource.py +0 -415
- dsp_tools/xmllib/models/values.py +0 -428
- dsp_tools-9.1.0.post11.dist-info/METADATA +0 -130
- dsp_tools-9.1.0.post11.dist-info/RECORD +0 -167
- dsp_tools-9.1.0.post11.dist-info/WHEEL +0 -4
- dsp_tools-9.1.0.post11.dist-info/licenses/LICENSE +0 -674
- /dsp_tools/{commands/excel2json/new_lists → clients}/__init__.py +0 -0
- /dsp_tools/commands/{excel2json/new_lists/models → create}/__init__.py +0 -0
- /dsp_tools/commands/{project → create/create_on_server}/__init__.py +0 -0
- /dsp_tools/commands/{project/create → create/models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → create/parsing}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate → create/serialisation}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/models → excel2json/lists}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/sparql → excel2json/lists/models}/__init__.py +0 -0
- /dsp_tools/commands/excel2json/{new_lists → lists}/models/deserialise.py +0 -0
- /dsp_tools/commands/{xmlupload/models/deserialise → get}/__init__.py +0 -0
- /dsp_tools/commands/{xmlupload/models/serialise → get/legacy_models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → get/legacy_models}/helpers.py +0 -0
- /dsp_tools/{models → commands/get/models}/__init__.py +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from rdflib import OWL
|
|
4
|
+
from rdflib import RDF
|
|
5
|
+
from rdflib import RDFS
|
|
6
|
+
from rdflib import BNode
|
|
7
|
+
from rdflib import Graph
|
|
8
|
+
from rdflib import Literal
|
|
9
|
+
from rdflib import URIRef
|
|
10
|
+
|
|
11
|
+
from dsp_tools.commands.create.create_on_server.mappers import PARSED_CARDINALITY_TO_RDF
|
|
12
|
+
from dsp_tools.commands.create.models.parsed_ontology import ParsedClass
|
|
13
|
+
from dsp_tools.commands.create.models.parsed_ontology import ParsedOntology
|
|
14
|
+
from dsp_tools.commands.create.models.parsed_ontology import ParsedProperty
|
|
15
|
+
from dsp_tools.commands.create.models.parsed_ontology import ParsedPropertyCardinality
|
|
16
|
+
from dsp_tools.utils.rdf_constants import KNORA_API
|
|
17
|
+
from dsp_tools.utils.rdf_constants import KNORA_API_PREFIX
|
|
18
|
+
from dsp_tools.utils.rdf_constants import SALSAH_GUI
|
|
19
|
+
from dsp_tools.utils.rdflib_utils import serialise_json
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def serialise_ontology_graph_for_request(parsed_ontology: ParsedOntology, project_iri: str) -> dict[str, Any]:
|
|
23
|
+
onto_graph = {
|
|
24
|
+
f"{KNORA_API_PREFIX}attachedToProject": {"@id": project_iri},
|
|
25
|
+
f"{KNORA_API_PREFIX}ontologyName": parsed_ontology.name,
|
|
26
|
+
str(RDFS.label): parsed_ontology.label,
|
|
27
|
+
}
|
|
28
|
+
if parsed_ontology.comment:
|
|
29
|
+
onto_graph[str(RDFS.comment)] = parsed_ontology.comment
|
|
30
|
+
return onto_graph
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _make_ontology_base_graph(onto_iri: URIRef, last_modification_date: Literal) -> Graph:
|
|
34
|
+
g = Graph()
|
|
35
|
+
g.add((onto_iri, RDF.type, OWL.Ontology))
|
|
36
|
+
g.add((onto_iri, KNORA_API.lastModificationDate, last_modification_date))
|
|
37
|
+
return g
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def serialise_cardinality_graph_for_request(
|
|
41
|
+
card: ParsedPropertyCardinality, res_iri: URIRef, onto_iri: URIRef, last_modification_date: Literal
|
|
42
|
+
) -> dict[str, Any]:
|
|
43
|
+
onto_g = _make_ontology_base_graph(onto_iri, last_modification_date)
|
|
44
|
+
onto_serialised = next(iter(serialise_json(onto_g)))
|
|
45
|
+
card_g = _make_one_cardinality_graph(card, res_iri)
|
|
46
|
+
card_serialised = serialise_json(card_g)
|
|
47
|
+
onto_serialised["@graph"] = card_serialised
|
|
48
|
+
return onto_serialised
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _make_one_cardinality_graph(card: ParsedPropertyCardinality, res_iri: URIRef) -> Graph:
|
|
52
|
+
card_info = PARSED_CARDINALITY_TO_RDF[card.cardinality]
|
|
53
|
+
g = Graph()
|
|
54
|
+
bn_card = BNode()
|
|
55
|
+
g.add((res_iri, RDF.type, OWL.Class))
|
|
56
|
+
g.add((res_iri, RDFS.subClassOf, bn_card))
|
|
57
|
+
g.add((bn_card, RDF.type, OWL.Restriction))
|
|
58
|
+
g.add((bn_card, card_info.owl_property, card_info.cardinality_value))
|
|
59
|
+
g.add((bn_card, OWL.onProperty, URIRef(card.propname)))
|
|
60
|
+
if card.gui_order is not None:
|
|
61
|
+
g.add((bn_card, SALSAH_GUI.guiOrder, Literal(card.gui_order)))
|
|
62
|
+
return g
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def serialise_property_graph_for_request(
|
|
66
|
+
prop: ParsedProperty, list_iri: Literal | None, onto_iri: URIRef, last_modification_date: Literal
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
onto_g = _make_ontology_base_graph(onto_iri, last_modification_date)
|
|
69
|
+
onto_serialised = next(iter(serialise_json(onto_g)))
|
|
70
|
+
prop_g = _make_one_property_graph(prop, list_iri)
|
|
71
|
+
prop_serialised = serialise_json(prop_g)
|
|
72
|
+
onto_serialised["@graph"] = prop_serialised
|
|
73
|
+
return onto_serialised
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _make_one_property_graph(prop: ParsedProperty, list_iri: Literal | None) -> Graph:
|
|
77
|
+
trips: list[tuple[URIRef, Literal | URIRef]] = [
|
|
78
|
+
(RDF.type, OWL.ObjectProperty),
|
|
79
|
+
(KNORA_API.objectType, URIRef(str(prop.object))),
|
|
80
|
+
(SALSAH_GUI.guiElement, URIRef(str(prop.gui_element))),
|
|
81
|
+
]
|
|
82
|
+
trips.extend([(RDFS.label, Literal(lbl, lang=lang_tag)) for lang_tag, lbl in prop.labels.items()])
|
|
83
|
+
if prop.comments:
|
|
84
|
+
trips.extend([(RDFS.comment, Literal(cmnt, lang=lang_tag)) for lang_tag, cmnt in prop.comments.items()])
|
|
85
|
+
trips.extend([(RDFS.subPropertyOf, URIRef(supr)) for supr in prop.supers])
|
|
86
|
+
if prop.subject:
|
|
87
|
+
trips.append((KNORA_API.subjectType, URIRef(prop.subject)))
|
|
88
|
+
if list_iri is not None:
|
|
89
|
+
trips.append((SALSAH_GUI.guiAttribute, list_iri))
|
|
90
|
+
prop_iri = URIRef(prop.name)
|
|
91
|
+
g = Graph()
|
|
92
|
+
for p, o in trips:
|
|
93
|
+
g.add((prop_iri, p, o))
|
|
94
|
+
return g
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def serialise_class_graph_for_request(
|
|
98
|
+
cls: ParsedClass, onto_iri: URIRef, last_modification_date: Literal
|
|
99
|
+
) -> dict[str, Any]:
|
|
100
|
+
onto_g = _make_ontology_base_graph(onto_iri, last_modification_date)
|
|
101
|
+
onto_serialised = next(iter(serialise_json(onto_g)))
|
|
102
|
+
cls_g = _make_one_class_graph(cls)
|
|
103
|
+
cls_serialised = serialise_json(cls_g)
|
|
104
|
+
onto_serialised["@graph"] = cls_serialised
|
|
105
|
+
return onto_serialised
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _make_one_class_graph(cls: ParsedClass) -> Graph:
|
|
109
|
+
trips: list[tuple[URIRef, Literal | URIRef]] = [(RDF.type, OWL.Class)]
|
|
110
|
+
trips.extend([(RDFS.label, Literal(lbl, lang=lang_tag)) for lang_tag, lbl in cls.labels.items()])
|
|
111
|
+
trips.extend([(RDFS.subClassOf, URIRef(x)) for x in cls.supers])
|
|
112
|
+
if cls.comments:
|
|
113
|
+
trips.extend([(RDFS.comment, Literal(cmnt, lang=lang_tag)) for lang_tag, cmnt in cls.comments.items()])
|
|
114
|
+
|
|
115
|
+
cls_iri = URIRef(cls.name)
|
|
116
|
+
g = Graph()
|
|
117
|
+
for p, o in trips:
|
|
118
|
+
g.add((cls_iri, p, o))
|
|
119
|
+
return g
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from dsp_tools.commands.create.models.parsed_project import ParsedGroup
|
|
4
|
+
from dsp_tools.commands.create.models.parsed_project import ParsedProjectMetadata
|
|
5
|
+
from dsp_tools.commands.create.models.parsed_project import ParsedUser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def serialise_one_group(group: ParsedGroup, project_iri: str) -> dict[str, Any]:
|
|
9
|
+
return {
|
|
10
|
+
"name": group.name,
|
|
11
|
+
"descriptions": [{"value": desc.text, "language": desc.lang} for desc in group.descriptions],
|
|
12
|
+
"project": project_iri,
|
|
13
|
+
"status": True,
|
|
14
|
+
"selfjoin": False,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def serialise_one_user_for_creation(user: ParsedUser) -> dict[str, Any]:
|
|
19
|
+
return {
|
|
20
|
+
"username": user.username,
|
|
21
|
+
"email": user.email,
|
|
22
|
+
"givenName": user.given_name,
|
|
23
|
+
"familyName": user.family_name,
|
|
24
|
+
"password": user.password,
|
|
25
|
+
"lang": user.lang,
|
|
26
|
+
"status": True,
|
|
27
|
+
"systemAdmin": False,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def serialise_project(project: ParsedProjectMetadata) -> dict[str, Any]:
|
|
32
|
+
descriptions = [{"value": value, "language": lang} for lang, value in project.descriptions.items()]
|
|
33
|
+
info = {
|
|
34
|
+
"shortcode": project.shortcode,
|
|
35
|
+
"shortname": project.shortname,
|
|
36
|
+
"longname": project.longname,
|
|
37
|
+
"description": descriptions,
|
|
38
|
+
"keywords": project.keywords,
|
|
39
|
+
"status": True,
|
|
40
|
+
"selfjoin": False,
|
|
41
|
+
}
|
|
42
|
+
if project.enabled_licenses:
|
|
43
|
+
info["enabledLicenses"] = project.enabled_licenses
|
|
44
|
+
return info
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Excel2JSON Module
|
|
2
|
+
|
|
3
|
+
This module converts Excel files into JSON project files for DSP-TOOLS.
|
|
4
|
+
|
|
5
|
+
## Module Structure
|
|
6
|
+
|
|
7
|
+
### Core Functions
|
|
8
|
+
|
|
9
|
+
- `excel2json/project.py` - Main entry point that orchestrates the conversion of Excel files to complete JSON project files
|
|
10
|
+
- `excel2json/properties.py` - Converts property definitions from Excel to JSON format
|
|
11
|
+
- `excel2json/resources.py` - Converts resource class definitions from Excel to JSON format
|
|
12
|
+
- `excel2json/json_header.py` - Processes project metadata from Excel
|
|
13
|
+
- `excel2json/lists.py` - Handles list definitions (hierarchical vocabularies)
|
|
14
|
+
|
|
15
|
+
### Models
|
|
16
|
+
|
|
17
|
+
- `models/ontology.py` - Data models for ontology elements (OntoProperty, OntoResource, etc.)
|
|
18
|
+
- `models/json_header.py` - Data models for project header information
|
|
19
|
+
- `models/input_error.py` - Error handling and validation models
|
|
20
|
+
- `lists/models/` - Models specific to list processing
|
|
21
|
+
|
|
22
|
+
### Utilities
|
|
23
|
+
|
|
24
|
+
- `utils.py` - Shared utility functions for Excel processing
|
|
25
|
+
- `lists/utils.py` - List-specific utilities
|
|
26
|
+
- `lists/compliance_checks.py` - Validation logic for list structures
|
|
27
|
+
|
|
28
|
+
## Key Design Patterns
|
|
29
|
+
|
|
30
|
+
### Excel Processing Pipeline
|
|
31
|
+
|
|
32
|
+
1. **Read and Clean** - Excel files are read and cleaned using pandas
|
|
33
|
+
2. **Validate Structure** - Column presence and data integrity checks
|
|
34
|
+
3. **Parse Rows** - Individual rows converted to model objects
|
|
35
|
+
4. **Serialize** - Model objects converted to JSON format
|
|
36
|
+
5. **Validate Output** - JSON schema validation against DSP specifications
|
|
37
|
+
|
|
38
|
+
### Error Handling
|
|
39
|
+
|
|
40
|
+
- Comprehensive error collection during parsing
|
|
41
|
+
- Position tracking for Excel cell-level error reporting
|
|
42
|
+
- Structured error models for different error types
|
|
43
|
+
- Non-blocking validation (collect all errors before failing)
|
|
44
|
+
|
|
45
|
+
### Optional Columns
|
|
46
|
+
|
|
47
|
+
- If the user has omitted optional Excel columns, the python code adds these columns to the pandas df,
|
|
48
|
+
using the `add_optional_columns()` utility
|
|
49
|
+
- Missing optional columns are added as empty to maintain consistent DataFrame structure
|
|
50
|
+
- Optional fields in models are handled with `foo | None` types
|
|
51
|
+
|
|
52
|
+
## Testing Strategy
|
|
53
|
+
|
|
54
|
+
### Unit Tests
|
|
55
|
+
|
|
56
|
+
- Test individual parsing functions with minimal data
|
|
57
|
+
- Mock DataFrame inputs for isolated testing
|
|
58
|
+
- Test error conditions and edge cases
|
|
59
|
+
- Test model serialization/deserialization
|
|
60
|
+
|
|
61
|
+
### Integration Tests
|
|
62
|
+
|
|
63
|
+
- Test with real Excel files from testdata
|
|
64
|
+
- End-to-end conversion validation
|
|
65
|
+
- JSON schema compliance testing
|
|
66
|
+
|
|
67
|
+
## Column Processing Pattern
|
|
68
|
+
|
|
69
|
+
When adding new optional columns:
|
|
70
|
+
|
|
71
|
+
1. **Define Column** - Add column name to expected columns list
|
|
72
|
+
2. **Add to Optional** - Use `add_optional_columns()` to handle missing optional columns
|
|
73
|
+
3. **Parse in Row Handler** - Extract value in `_get_*_from_row()` functions
|
|
74
|
+
4. **Update Model** - Add field to corresponding dataclass model
|
|
75
|
+
5. **Serialize** - Include in `serialise()` method when present
|
|
76
|
+
6. **Test** - Unit and integration tests for new functionality
|
|
77
|
+
|
|
78
|
+
## Common Patterns
|
|
79
|
+
|
|
80
|
+
### Reading Excel Files
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
df_dict = read_and_clean_all_sheets(excel_file)
|
|
84
|
+
df = add_optional_columns(df, optional_columns)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Parsing Optional Values
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
value = None if pd.isna(row["optional_column"]) else str(row["optional_column"]).strip()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Model Serialization
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
def serialise(self) -> dict[str, Any]:
|
|
97
|
+
result = {"required_field": self.required_field}
|
|
98
|
+
if self.optional_field:
|
|
99
|
+
result["optional_field"] = self.optional_field
|
|
100
|
+
return result
|
|
101
|
+
```
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import warnings
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import pandas as pd
|
|
@@ -13,23 +14,24 @@ from dsp_tools.commands.excel2json.models.input_error import InvalidExcelContent
|
|
|
13
14
|
from dsp_tools.commands.excel2json.models.input_error import MissingValuesProblem
|
|
14
15
|
from dsp_tools.commands.excel2json.models.input_error import MoreThanOneRowProblem
|
|
15
16
|
from dsp_tools.commands.excel2json.models.input_error import PositionInExcel
|
|
16
|
-
from dsp_tools.commands.excel2json.models.input_error import Problem
|
|
17
17
|
from dsp_tools.commands.excel2json.models.input_error import RequiredColumnMissingProblem
|
|
18
18
|
from dsp_tools.commands.excel2json.models.json_header import Descriptions
|
|
19
19
|
from dsp_tools.commands.excel2json.models.json_header import EmptyJsonHeader
|
|
20
20
|
from dsp_tools.commands.excel2json.models.json_header import FilledJsonHeader
|
|
21
21
|
from dsp_tools.commands.excel2json.models.json_header import JsonHeader
|
|
22
22
|
from dsp_tools.commands.excel2json.models.json_header import Keywords
|
|
23
|
+
from dsp_tools.commands.excel2json.models.json_header import Licenses
|
|
23
24
|
from dsp_tools.commands.excel2json.models.json_header import Prefixes
|
|
24
25
|
from dsp_tools.commands.excel2json.models.json_header import Project
|
|
25
26
|
from dsp_tools.commands.excel2json.models.json_header import User
|
|
26
|
-
from dsp_tools.commands.excel2json.models.json_header import UserRole
|
|
27
27
|
from dsp_tools.commands.excel2json.models.json_header import Users
|
|
28
28
|
from dsp_tools.commands.excel2json.utils import check_contains_required_columns
|
|
29
29
|
from dsp_tools.commands.excel2json.utils import find_missing_required_values
|
|
30
30
|
from dsp_tools.commands.excel2json.utils import read_and_clean_all_sheets
|
|
31
|
-
from dsp_tools.
|
|
32
|
-
from dsp_tools.
|
|
31
|
+
from dsp_tools.error.custom_warnings import DspToolsFutureWarning
|
|
32
|
+
from dsp_tools.error.exceptions import InputError
|
|
33
|
+
from dsp_tools.error.problems import Problem
|
|
34
|
+
from dsp_tools.utils.data_formats.uri_util import is_uri
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def get_json_header(excel_filepath: Path) -> JsonHeader:
|
|
@@ -59,6 +61,7 @@ def get_json_header(excel_filepath: Path) -> JsonHeader:
|
|
|
59
61
|
|
|
60
62
|
|
|
61
63
|
def _do_all_checks(df_dict: dict[str, pd.DataFrame]) -> ExcelFileProblem | None:
|
|
64
|
+
_futurewarning_about_inexistent_license_info(df_dict)
|
|
62
65
|
if file_problems := _check_if_sheets_are_filled_and_exist(df_dict):
|
|
63
66
|
return file_problems
|
|
64
67
|
sheet_problems: list[Problem] = []
|
|
@@ -70,6 +73,9 @@ def _do_all_checks(df_dict: dict[str, pd.DataFrame]) -> ExcelFileProblem | None:
|
|
|
70
73
|
sheet_problems.append(description_problem)
|
|
71
74
|
if keywords_problem := _check_keywords(df_dict["keywords"]):
|
|
72
75
|
sheet_problems.append(keywords_problem)
|
|
76
|
+
if (licenses_df := df_dict.get("licenses")) is not None:
|
|
77
|
+
if license_problem := _check_licenses(licenses_df):
|
|
78
|
+
sheet_problems.append(license_problem)
|
|
73
79
|
if (user_df := df_dict.get("users")) is not None:
|
|
74
80
|
if user_problems := _check_all_users(user_df):
|
|
75
81
|
sheet_problems.append(user_problems)
|
|
@@ -78,6 +84,15 @@ def _do_all_checks(df_dict: dict[str, pd.DataFrame]) -> ExcelFileProblem | None:
|
|
|
78
84
|
return None
|
|
79
85
|
|
|
80
86
|
|
|
87
|
+
def _futurewarning_about_inexistent_license_info(df_dict: dict[str, pd.DataFrame]) -> None:
|
|
88
|
+
if df_dict.get("licenses") is None:
|
|
89
|
+
msg = (
|
|
90
|
+
"The json_header.xlsx file does not have a sheet containing the enabled licenses. "
|
|
91
|
+
"Please note that this will become mandatory in the future."
|
|
92
|
+
)
|
|
93
|
+
warnings.warn(DspToolsFutureWarning(msg))
|
|
94
|
+
|
|
95
|
+
|
|
81
96
|
def _check_if_sheets_are_filled_and_exist(df_dict: dict[str, pd.DataFrame]) -> ExcelFileProblem | None:
|
|
82
97
|
expected_sheets = ["prefixes", "project", "description", "keywords"]
|
|
83
98
|
|
|
@@ -117,7 +132,7 @@ def _check_prefixes(df: pd.DataFrame) -> ExcelSheetProblem | None:
|
|
|
117
132
|
|
|
118
133
|
def _check_project_sheet(df: pd.DataFrame) -> ExcelSheetProblem | None:
|
|
119
134
|
problems: list[Problem] = []
|
|
120
|
-
cols = {"shortcode", "shortname", "longname"}
|
|
135
|
+
cols = {"shortcode", "shortname", "longname", "default_permissions"}
|
|
121
136
|
if missing_cols := check_contains_required_columns(df, cols):
|
|
122
137
|
problems.append(missing_cols)
|
|
123
138
|
if len(df) > 1:
|
|
@@ -126,6 +141,14 @@ def _check_project_sheet(df: pd.DataFrame) -> ExcelSheetProblem | None:
|
|
|
126
141
|
return ExcelSheetProblem("project", problems)
|
|
127
142
|
if missing_values := find_missing_required_values(df, list(cols)):
|
|
128
143
|
return ExcelSheetProblem("project", [MissingValuesProblem(missing_values)])
|
|
144
|
+
perm_value = str(df.loc[0, "default_permissions"]).strip().lower()
|
|
145
|
+
if perm_value not in ["public", "private"]:
|
|
146
|
+
prob = InvalidExcelContentProblem(
|
|
147
|
+
expected_content="One of: public, private",
|
|
148
|
+
actual_content=perm_value,
|
|
149
|
+
excel_position=PositionInExcel(column="default_permissions", row=2),
|
|
150
|
+
)
|
|
151
|
+
return ExcelSheetProblem("project", [prob])
|
|
129
152
|
return None
|
|
130
153
|
|
|
131
154
|
|
|
@@ -148,13 +171,20 @@ def _check_keywords(df: pd.DataFrame) -> ExcelSheetProblem | None:
|
|
|
148
171
|
return None
|
|
149
172
|
|
|
150
173
|
|
|
174
|
+
def _check_licenses(df: pd.DataFrame) -> ExcelSheetProblem | None:
|
|
175
|
+
if missing_cols := check_contains_required_columns(df, {"enabled"}):
|
|
176
|
+
return ExcelSheetProblem("licenses", [missing_cols])
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
|
|
151
180
|
def _check_all_users(df: pd.DataFrame) -> ExcelSheetProblem | None:
|
|
152
181
|
if not len(df) > 0:
|
|
153
182
|
return None
|
|
154
|
-
|
|
155
|
-
if missing_cols := check_contains_required_columns(df, set(
|
|
183
|
+
required_columns = ["username", "email", "givenname", "password", "familyname", "lang", "role"]
|
|
184
|
+
if missing_cols := check_contains_required_columns(df, set(required_columns)):
|
|
156
185
|
return ExcelSheetProblem("users", [missing_cols])
|
|
157
|
-
|
|
186
|
+
required_values = ["username", "email", "givenname", "familyname", "lang", "role"]
|
|
187
|
+
if missing_vals := find_missing_required_values(df, required_values):
|
|
158
188
|
return ExcelSheetProblem("users", [MissingValuesProblem(missing_vals)])
|
|
159
189
|
problems: list[Problem] = []
|
|
160
190
|
for i, row in df.iterrows():
|
|
@@ -198,10 +228,10 @@ def _check_email(email: str, row_num: int) -> InvalidExcelContentProblem | None:
|
|
|
198
228
|
|
|
199
229
|
|
|
200
230
|
def _check_role(value: str, row_num: int) -> InvalidExcelContentProblem | None:
|
|
201
|
-
possible_roles = ["projectadmin", "
|
|
231
|
+
possible_roles = ["projectadmin", "projectmember"]
|
|
202
232
|
if value.lower() not in possible_roles:
|
|
203
233
|
return InvalidExcelContentProblem(
|
|
204
|
-
expected_content="One of: projectadmin,
|
|
234
|
+
expected_content="One of: projectadmin, projectmember",
|
|
205
235
|
actual_content=value,
|
|
206
236
|
excel_position=PositionInExcel(column="role", row=row_num),
|
|
207
237
|
)
|
|
@@ -224,18 +254,25 @@ def _extract_project(df_dict: dict[str, pd.DataFrame]) -> Project:
|
|
|
224
254
|
project_df = df_dict["project"]
|
|
225
255
|
extracted_description = _extract_descriptions(df_dict["description"])
|
|
226
256
|
extracted_keywords = _extract_keywords(df_dict["keywords"])
|
|
257
|
+
if (lic_df := df_dict.get("licenses")) is not None:
|
|
258
|
+
extracted_licenses = _extract_licenses(lic_df)
|
|
259
|
+
else:
|
|
260
|
+
extracted_licenses = Licenses([])
|
|
227
261
|
all_users = None
|
|
228
262
|
if (user_df := df_dict.get("users")) is not None:
|
|
229
263
|
if len(user_df) > 0:
|
|
230
264
|
all_users = _extract_users(user_df)
|
|
231
265
|
shortcode = str(project_df.loc[0, "shortcode"]).zfill(4)
|
|
266
|
+
default_permissions = str(project_df.loc[0, "default_permissions"]).strip().lower()
|
|
232
267
|
return Project(
|
|
233
268
|
shortcode=shortcode,
|
|
234
269
|
shortname=str(project_df.loc[0, "shortname"]),
|
|
235
270
|
longname=str(project_df.loc[0, "longname"]),
|
|
236
271
|
descriptions=extracted_description,
|
|
237
272
|
keywords=extracted_keywords,
|
|
273
|
+
licenses=extracted_licenses,
|
|
238
274
|
users=all_users,
|
|
275
|
+
default_permissions=default_permissions,
|
|
239
276
|
)
|
|
240
277
|
|
|
241
278
|
|
|
@@ -256,6 +293,11 @@ def _extract_keywords(df: pd.DataFrame) -> Keywords:
|
|
|
256
293
|
return Keywords(keywords)
|
|
257
294
|
|
|
258
295
|
|
|
296
|
+
def _extract_licenses(df: pd.DataFrame) -> Licenses:
|
|
297
|
+
licenses = list({x for x in df["enabled"] if not pd.isna(x)})
|
|
298
|
+
return Licenses(licenses)
|
|
299
|
+
|
|
300
|
+
|
|
259
301
|
def _extract_users(df: pd.DataFrame) -> Users:
|
|
260
302
|
users = []
|
|
261
303
|
for _, row in df.iterrows():
|
|
@@ -265,23 +307,15 @@ def _extract_users(df: pd.DataFrame) -> Users:
|
|
|
265
307
|
|
|
266
308
|
|
|
267
309
|
def _extract_one_user(row: pd.Series[str]) -> User:
|
|
268
|
-
|
|
310
|
+
isProjectAdmin = row["role"].lower() == "projectadmin"
|
|
311
|
+
if pd.isna(pw := row["password"]):
|
|
312
|
+
pw = None
|
|
269
313
|
return User(
|
|
270
314
|
username=row["username"],
|
|
271
315
|
email=row["email"],
|
|
272
316
|
givenName=row["givenname"],
|
|
273
317
|
familyName=row["familyname"],
|
|
274
|
-
password=
|
|
318
|
+
password=pw,
|
|
275
319
|
lang=row["lang"],
|
|
276
|
-
|
|
320
|
+
isProjectAdmin=isProjectAdmin,
|
|
277
321
|
)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
def _get_role(value: str) -> UserRole:
|
|
281
|
-
match value.lower():
|
|
282
|
-
case "projectadmin":
|
|
283
|
-
return UserRole(project_admin=True)
|
|
284
|
-
case "systemadmin":
|
|
285
|
-
return UserRole(sys_admin=True)
|
|
286
|
-
case _:
|
|
287
|
-
return UserRole()
|
|
@@ -9,32 +9,32 @@ import pandas as pd
|
|
|
9
9
|
import regex
|
|
10
10
|
from loguru import logger
|
|
11
11
|
|
|
12
|
+
from dsp_tools.commands.excel2json.lists.models.deserialise import Columns
|
|
13
|
+
from dsp_tools.commands.excel2json.lists.models.deserialise import ExcelSheet
|
|
14
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import CollectedSheetProblems
|
|
15
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import DuplicateIDProblem
|
|
16
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import DuplicatesCustomIDInProblem
|
|
17
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import DuplicatesInSheetProblem
|
|
18
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import DuplicatesListNameProblem
|
|
19
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import ListCreationProblem
|
|
20
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import ListInformation
|
|
21
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import ListSheetComplianceProblem
|
|
22
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import ListSheetContentProblem
|
|
23
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import MinimumRowsProblem
|
|
24
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import MissingExpectedColumn
|
|
25
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import MissingNodeColumn
|
|
26
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import MissingNodeTranslationProblem
|
|
27
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import MissingTranslationsSheetProblem
|
|
28
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import MultipleListPerSheetProblem
|
|
29
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import NodesPerRowProblem
|
|
30
|
+
from dsp_tools.commands.excel2json.lists.models.input_error import SheetProblem
|
|
31
|
+
from dsp_tools.commands.excel2json.lists.utils import get_columns_of_preferred_lang
|
|
32
|
+
from dsp_tools.commands.excel2json.lists.utils import get_hierarchy_nums
|
|
33
|
+
from dsp_tools.commands.excel2json.lists.utils import get_lang_string_from_column_name
|
|
12
34
|
from dsp_tools.commands.excel2json.models.input_error import PositionInExcel
|
|
13
|
-
from dsp_tools.
|
|
14
|
-
from dsp_tools.
|
|
15
|
-
from dsp_tools.
|
|
16
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import CollectedSheetProblems
|
|
17
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import DuplicateIDProblem
|
|
18
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import DuplicatesCustomIDInProblem
|
|
19
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import DuplicatesInSheetProblem
|
|
20
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import DuplicatesListNameProblem
|
|
21
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import ListCreationProblem
|
|
22
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import ListInformation
|
|
23
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import ListSheetComplianceProblem
|
|
24
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import ListSheetContentProblem
|
|
25
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import MinimumRowsProblem
|
|
26
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import MissingExpectedColumn
|
|
27
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import MissingNodeColumn
|
|
28
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import MissingNodeTranslationProblem
|
|
29
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import MissingTranslationsSheetProblem
|
|
30
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import MultipleListPerSheetProblem
|
|
31
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import NodesPerRowProblem
|
|
32
|
-
from dsp_tools.commands.excel2json.new_lists.models.input_error import SheetProblem
|
|
33
|
-
from dsp_tools.commands.excel2json.new_lists.utils import get_columns_of_preferred_lang
|
|
34
|
-
from dsp_tools.commands.excel2json.new_lists.utils import get_hierarchy_nums
|
|
35
|
-
from dsp_tools.commands.excel2json.new_lists.utils import get_lang_string_from_column_name
|
|
36
|
-
from dsp_tools.models.custom_warnings import DspToolsUserWarning
|
|
37
|
-
from dsp_tools.models.exceptions import InputError
|
|
35
|
+
from dsp_tools.error.custom_warnings import DspToolsUserWarning
|
|
36
|
+
from dsp_tools.error.exceptions import InputError
|
|
37
|
+
from dsp_tools.error.problems import Problem
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def make_all_excel_compliance_checks(sheet_list: list[ExcelSheet]) -> None:
|
|
@@ -277,7 +277,7 @@ def _check_for_erroneous_entries_one_column_level(
|
|
|
277
277
|
def _check_for_erroneous_entries_one_grouped_df(
|
|
278
278
|
group: pd.DataFrame, target_cols: list[str]
|
|
279
279
|
) -> list[NodesPerRowProblem]:
|
|
280
|
-
problems = []
|
|
280
|
+
problems: list[NodesPerRowProblem] = []
|
|
281
281
|
first_col = min(group.index)
|
|
282
282
|
# The first row is the current parent node. The remaining columns in that row must be empty.
|
|
283
283
|
if not group.loc[first_col, target_cols[1:]].isna().all():
|
|
@@ -9,20 +9,20 @@ from typing import Optional
|
|
|
9
9
|
import pandas as pd
|
|
10
10
|
import regex
|
|
11
11
|
|
|
12
|
-
from dsp_tools.commands.excel2json.lists import
|
|
13
|
-
from dsp_tools.commands.excel2json.
|
|
14
|
-
from dsp_tools.commands.excel2json.
|
|
15
|
-
from dsp_tools.commands.excel2json.
|
|
16
|
-
from dsp_tools.commands.excel2json.
|
|
17
|
-
from dsp_tools.commands.excel2json.
|
|
18
|
-
from dsp_tools.commands.excel2json.
|
|
19
|
-
from dsp_tools.commands.excel2json.
|
|
20
|
-
from dsp_tools.commands.excel2json.
|
|
12
|
+
from dsp_tools.commands.excel2json.lists.compliance_checks import make_all_excel_compliance_checks
|
|
13
|
+
from dsp_tools.commands.excel2json.lists.models.deserialise import Columns
|
|
14
|
+
from dsp_tools.commands.excel2json.lists.models.deserialise import ExcelSheet
|
|
15
|
+
from dsp_tools.commands.excel2json.lists.models.serialise import ListNode
|
|
16
|
+
from dsp_tools.commands.excel2json.lists.models.serialise import ListRoot
|
|
17
|
+
from dsp_tools.commands.excel2json.lists.utils import get_column_info
|
|
18
|
+
from dsp_tools.commands.excel2json.lists.utils import get_columns_of_preferred_lang
|
|
19
|
+
from dsp_tools.commands.excel2json.lists.utils import get_lang_string_from_column_name
|
|
20
|
+
from dsp_tools.commands.excel2json.old_lists import validate_lists_section_with_schema
|
|
21
21
|
from dsp_tools.commands.excel2json.utils import add_optional_columns
|
|
22
22
|
from dsp_tools.commands.excel2json.utils import read_and_clean_all_sheets
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def
|
|
25
|
+
def excel2lists(
|
|
26
26
|
excelfolder: str | Path,
|
|
27
27
|
path_to_output_file: Optional[Path] = None,
|
|
28
28
|
) -> tuple[list[dict[str, Any]], bool]:
|
|
@@ -132,7 +132,7 @@ def _remove_duplicate_ids_in_all_excels(duplicate_ids: list[str], sheet_list: li
|
|
|
132
132
|
df = sheet.df
|
|
133
133
|
for i, row in df.iterrows():
|
|
134
134
|
if row["id"] in duplicate_ids and pd.isna(row["id (optional)"]):
|
|
135
|
-
df.
|
|
135
|
+
df.loc[i, "id"] = _construct_non_duplicate_id_string(row, sheet.col_info.preferred_lang) # type: ignore[index]
|
|
136
136
|
all_sheets.append(
|
|
137
137
|
ExcelSheet(excel_name=sheet.excel_name, sheet_name=sheet.sheet_name, col_info=sheet.col_info, df=df)
|
|
138
138
|
)
|
|
@@ -150,8 +150,8 @@ def _resolve_duplicate_ids_keep_custom_change_auto_id_one_df(df: pd.DataFrame, p
|
|
|
150
150
|
"""If there are duplicates in the id column, the auto_id is changed, the custom ID remains the same."""
|
|
151
151
|
if (duplicate_filter := df["id"].duplicated(keep=False)).any():
|
|
152
152
|
for i in duplicate_filter.index[duplicate_filter]:
|
|
153
|
-
if pd.isna(df.
|
|
154
|
-
df.loc[i, "id"] = _construct_non_duplicate_id_string(df.
|
|
153
|
+
if pd.isna(df.loc[i, "id (optional)"]):
|
|
154
|
+
df.loc[i, "id"] = _construct_non_duplicate_id_string(df.loc[i], preferred_language)
|
|
155
155
|
return df
|
|
156
156
|
|
|
157
157
|
|
|
@@ -167,7 +167,7 @@ def _create_auto_id_one_df(df: pd.DataFrame, preferred_language: str) -> pd.Data
|
|
|
167
167
|
if pd.isna(row["id (optional)"]):
|
|
168
168
|
for col in column_names:
|
|
169
169
|
if pd.notna(row[col]):
|
|
170
|
-
df.
|
|
170
|
+
df.loc[i, "auto_id"] = row[col] # type: ignore[index]
|
|
171
171
|
break
|
|
172
172
|
df = _resolve_duplicate_ids_for_auto_id_one_df(df, preferred_language)
|
|
173
173
|
return df
|
|
@@ -177,7 +177,7 @@ def _resolve_duplicate_ids_for_auto_id_one_df(df: pd.DataFrame, preferred_langua
|
|
|
177
177
|
"""In case the auto_id is not unique; both auto_ids get a new ID by joining the node names of all the ancestors."""
|
|
178
178
|
if (duplicate_filter := df["auto_id"].dropna().duplicated(keep=False)).any():
|
|
179
179
|
for i in duplicate_filter.index[duplicate_filter]:
|
|
180
|
-
df.
|
|
180
|
+
df.loc[i, "auto_id"] = _construct_non_duplicate_id_string(df.loc[i], preferred_language)
|
|
181
181
|
return df
|
|
182
182
|
|
|
183
183
|
|
|
@@ -190,17 +190,18 @@ def _construct_non_duplicate_id_string(row: pd.Series[Any], preferred_language:
|
|
|
190
190
|
|
|
191
191
|
|
|
192
192
|
def _make_serialised_lists(sheet_list: list[ExcelSheet]) -> list[dict[str, Any]]:
|
|
193
|
-
all_lists = []
|
|
193
|
+
all_lists: list[ListRoot] = []
|
|
194
194
|
for sheet in sheet_list:
|
|
195
195
|
all_lists.append(_make_one_list(sheet))
|
|
196
|
+
all_lists = sorted(all_lists, key=lambda x: x.id_)
|
|
196
197
|
return [list_.to_dict() for list_ in all_lists]
|
|
197
198
|
|
|
198
199
|
|
|
199
200
|
def _make_one_list(sheet: ExcelSheet) -> ListRoot:
|
|
200
201
|
node_dict = _make_list_nodes_from_df(sheet.df, sheet.col_info)
|
|
201
|
-
nodes_for_root = _add_nodes_to_parent(node_dict, sheet.df.at[0, "id"]) if node_dict else []
|
|
202
|
+
nodes_for_root = _add_nodes_to_parent(node_dict, str(sheet.df.at[0, "id"])) if node_dict else []
|
|
202
203
|
return ListRoot(
|
|
203
|
-
id_=sheet.df.at[0, "id"],
|
|
204
|
+
id_=str(sheet.df.at[0, "id"]),
|
|
204
205
|
labels=_get_lang_dict(sheet.df.iloc[0], sheet.col_info.list_cols),
|
|
205
206
|
nodes=nodes_for_root,
|
|
206
207
|
comments=_get_lang_dict(sheet.df.iloc[0], sheet.col_info.comment_cols),
|
|
@@ -7,10 +7,10 @@ from typing import Protocol
|
|
|
7
7
|
|
|
8
8
|
from dsp_tools.commands.excel2json.models.input_error import ExcelFileProblem
|
|
9
9
|
from dsp_tools.commands.excel2json.models.input_error import PositionInExcel
|
|
10
|
-
from dsp_tools.commands.excel2json.models.input_error import Problem
|
|
11
10
|
from dsp_tools.commands.excel2json.models.input_error import grand_separator
|
|
12
11
|
from dsp_tools.commands.excel2json.models.input_error import list_separator
|
|
13
12
|
from dsp_tools.commands.excel2json.models.input_error import medium_separator
|
|
13
|
+
from dsp_tools.error.problems import Problem
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@dataclass(frozen=True)
|
|
@@ -23,17 +23,6 @@ class ListCreationProblem:
|
|
|
23
23
|
return title + grand_separator.join(msg)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
@dataclass(frozen=True)
|
|
27
|
-
class ListNodeProblem:
|
|
28
|
-
node_id: str
|
|
29
|
-
problems: dict[str, str]
|
|
30
|
-
|
|
31
|
-
def execute_error_protocol(self) -> str:
|
|
32
|
-
msg = [f"The node '{self.node_id}' has the following problem(s):"]
|
|
33
|
-
msg.extend([f"Field: '{key}', Problem: {value}" for key, value in self.problems.items()])
|
|
34
|
-
return list_separator.join(msg)
|
|
35
|
-
|
|
36
|
-
|
|
37
26
|
@dataclass
|
|
38
27
|
class SheetProblem(Protocol):
|
|
39
28
|
excel_name: str
|