dsp-tools 9.1.0.post11__py3-none-any.whl → 18.3.0.post13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsp_tools/__init__.py +4 -0
- dsp_tools/cli/args.py +36 -0
- dsp_tools/cli/call_action.py +51 -231
- dsp_tools/cli/call_action_files_only.py +101 -0
- dsp_tools/cli/call_action_with_network.py +207 -0
- dsp_tools/cli/create_parsers.py +156 -58
- dsp_tools/cli/entry_point.py +56 -26
- dsp_tools/cli/utils.py +87 -0
- dsp_tools/clients/CLAUDE.md +420 -0
- dsp_tools/clients/authentication_client.py +14 -0
- dsp_tools/clients/authentication_client_live.py +66 -0
- dsp_tools/{utils → clients}/connection.py +2 -18
- dsp_tools/clients/connection_live.py +233 -0
- dsp_tools/clients/fuseki_metrics.py +60 -0
- dsp_tools/clients/group_user_clients.py +35 -0
- dsp_tools/clients/group_user_clients_live.py +181 -0
- dsp_tools/clients/legal_info_client.py +23 -0
- dsp_tools/clients/legal_info_client_live.py +132 -0
- dsp_tools/clients/list_client.py +49 -0
- dsp_tools/clients/list_client_live.py +166 -0
- dsp_tools/clients/metadata_client.py +24 -0
- dsp_tools/clients/metadata_client_live.py +47 -0
- dsp_tools/clients/ontology_clients.py +49 -0
- dsp_tools/clients/ontology_create_client_live.py +166 -0
- dsp_tools/clients/ontology_get_client_live.py +80 -0
- dsp_tools/clients/permissions_client.py +68 -0
- dsp_tools/clients/project_client.py +16 -0
- dsp_tools/clients/project_client_live.py +66 -0
- dsp_tools/commands/create/communicate_problems.py +24 -0
- dsp_tools/commands/create/create.py +134 -0
- dsp_tools/commands/create/create_on_server/cardinalities.py +111 -0
- dsp_tools/commands/create/create_on_server/classes.py +99 -0
- dsp_tools/commands/create/create_on_server/complete_ontologies.py +116 -0
- dsp_tools/commands/create/create_on_server/default_permissions.py +134 -0
- dsp_tools/commands/create/create_on_server/group_users.py +165 -0
- dsp_tools/commands/create/create_on_server/lists.py +163 -0
- dsp_tools/commands/create/create_on_server/mappers.py +12 -0
- dsp_tools/commands/create/create_on_server/onto_utils.py +74 -0
- dsp_tools/commands/create/create_on_server/ontology.py +52 -0
- dsp_tools/commands/create/create_on_server/project.py +68 -0
- dsp_tools/commands/create/create_on_server/properties.py +119 -0
- dsp_tools/commands/create/exceptions.py +29 -0
- dsp_tools/commands/create/lists_only.py +66 -0
- dsp_tools/commands/create/models/create_problems.py +87 -0
- dsp_tools/commands/create/models/parsed_ontology.py +88 -0
- dsp_tools/commands/create/models/parsed_project.py +81 -0
- dsp_tools/commands/create/models/rdf_ontology.py +12 -0
- dsp_tools/commands/create/models/server_project_info.py +100 -0
- dsp_tools/commands/create/parsing/parse_lists.py +45 -0
- dsp_tools/commands/create/parsing/parse_ontology.py +243 -0
- dsp_tools/commands/create/parsing/parse_project.py +149 -0
- dsp_tools/commands/create/parsing/parsing_utils.py +40 -0
- dsp_tools/commands/create/project_validate.py +595 -0
- dsp_tools/commands/create/serialisation/ontology.py +119 -0
- dsp_tools/commands/create/serialisation/project.py +44 -0
- dsp_tools/commands/excel2json/CLAUDE.md +101 -0
- dsp_tools/commands/excel2json/json_header.py +57 -23
- dsp_tools/commands/excel2json/{new_lists → lists}/compliance_checks.py +26 -26
- dsp_tools/commands/excel2json/{new_lists/make_new_lists.py → lists/make_lists.py} +19 -18
- dsp_tools/commands/excel2json/{new_lists → lists}/models/input_error.py +1 -12
- dsp_tools/commands/excel2json/{new_lists → lists}/models/serialise.py +9 -5
- dsp_tools/commands/excel2json/{new_lists → lists}/utils.py +4 -4
- dsp_tools/commands/excel2json/models/input_error.py +31 -11
- dsp_tools/commands/excel2json/models/json_header.py +53 -15
- dsp_tools/commands/excel2json/models/ontology.py +4 -3
- dsp_tools/commands/excel2json/{lists.py → old_lists.py} +26 -112
- dsp_tools/commands/excel2json/project.py +78 -34
- dsp_tools/commands/excel2json/properties.py +57 -36
- dsp_tools/commands/excel2json/resources.py +32 -12
- dsp_tools/commands/excel2json/utils.py +20 -1
- dsp_tools/commands/excel2xml/__init__.py +2 -2
- dsp_tools/commands/excel2xml/excel2xml_cli.py +7 -15
- dsp_tools/commands/excel2xml/excel2xml_lib.py +138 -493
- dsp_tools/commands/excel2xml/propertyelement.py +5 -5
- dsp_tools/commands/{project → get}/get.py +29 -13
- dsp_tools/commands/get/get_permissions.py +257 -0
- dsp_tools/commands/get/get_permissions_legacy.py +89 -0
- dsp_tools/commands/{project/models → get/legacy_models}/context.py +6 -6
- dsp_tools/commands/{project/models → get/legacy_models}/group.py +5 -10
- dsp_tools/commands/{project/models → get/legacy_models}/listnode.py +5 -35
- dsp_tools/commands/{project/models → get/legacy_models}/model.py +1 -1
- dsp_tools/commands/{project/models → get/legacy_models}/ontology.py +9 -14
- dsp_tools/commands/{project/models → get/legacy_models}/project.py +13 -6
- dsp_tools/commands/{project/models → get/legacy_models}/propertyclass.py +9 -16
- dsp_tools/commands/{project/models → get/legacy_models}/resourceclass.py +8 -46
- dsp_tools/commands/{project/models → get/legacy_models}/user.py +19 -60
- dsp_tools/commands/get/models/permissions_models.py +10 -0
- dsp_tools/commands/id2iri.py +20 -10
- dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +81 -56
- dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +4 -10
- dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +97 -37
- dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +2 -2
- dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +9 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +3 -3
- dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +2 -10
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +12 -2
- dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +8 -9
- dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +18 -18
- dsp_tools/commands/start_stack.py +126 -77
- dsp_tools/commands/update_legal/CLAUDE.md +344 -0
- dsp_tools/commands/update_legal/__init__.py +0 -0
- dsp_tools/commands/update_legal/core.py +182 -0
- dsp_tools/commands/update_legal/csv_operations.py +135 -0
- dsp_tools/commands/update_legal/models.py +87 -0
- dsp_tools/commands/update_legal/xml_operations.py +247 -0
- dsp_tools/commands/validate_data/CLAUDE.md +159 -0
- dsp_tools/commands/validate_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/constants.py +59 -0
- dsp_tools/commands/validate_data/mappers.py +143 -0
- dsp_tools/commands/validate_data/models/__init__.py +0 -0
- dsp_tools/commands/validate_data/models/api_responses.py +45 -0
- dsp_tools/commands/validate_data/models/input_problems.py +119 -0
- dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
- dsp_tools/commands/validate_data/models/validation.py +106 -0
- dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
- dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
- dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
- dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
- dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
- dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
- dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
- dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
- dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
- dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
- dsp_tools/commands/{xml_validate/sparql/resource_shacl.py → validate_data/sparql/cardinality_shacl.py} +45 -47
- dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
- dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
- dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
- dsp_tools/commands/validate_data/utils.py +59 -0
- dsp_tools/commands/validate_data/validate_data.py +283 -0
- dsp_tools/commands/validate_data/validation/__init__.py +0 -0
- dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
- dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
- dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
- dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
- dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
- dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
- dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
- dsp_tools/commands/xmlupload/models/formatted_text_value.py +0 -25
- dsp_tools/commands/xmlupload/models/ingest.py +56 -70
- dsp_tools/commands/xmlupload/models/input_problems.py +6 -14
- dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
- dsp_tools/commands/xmlupload/models/permission.py +0 -39
- dsp_tools/commands/xmlupload/models/{deserialise/xmlpermission.py → permissions_parsed.py} +2 -2
- dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
- dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
- dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
- dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
- dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
- dsp_tools/commands/xmlupload/models/upload_clients.py +3 -3
- dsp_tools/commands/xmlupload/models/upload_state.py +2 -4
- dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
- dsp_tools/commands/xmlupload/{ark2iri.py → prepare_xml_input/ark2iri.py} +1 -1
- dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
- dsp_tools/commands/xmlupload/{iiif_uri_validator.py → prepare_xml_input/iiif_uri_validator.py} +2 -14
- dsp_tools/commands/xmlupload/{list_client.py → prepare_xml_input/list_client.py} +15 -10
- dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
- dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
- dsp_tools/commands/xmlupload/resource_create_client.py +7 -468
- dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
- dsp_tools/commands/xmlupload/stash/{construct_and_analyze_graph.py → analyse_circular_reference_graph.py} +64 -157
- dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
- dsp_tools/commands/xmlupload/stash/graph_models.py +13 -8
- dsp_tools/commands/xmlupload/stash/stash_circular_references.py +48 -115
- dsp_tools/commands/xmlupload/stash/stash_models.py +4 -9
- dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +34 -40
- dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +98 -108
- dsp_tools/commands/xmlupload/upload_config.py +8 -0
- dsp_tools/commands/xmlupload/write_diagnostic_info.py +14 -9
- dsp_tools/commands/xmlupload/xmlupload.py +214 -192
- dsp_tools/config/__init__.py +0 -0
- dsp_tools/config/logger_config.py +69 -0
- dsp_tools/{utils → config}/warnings_config.py +4 -1
- dsp_tools/error/__init__.py +0 -0
- dsp_tools/error/custom_warnings.py +39 -0
- dsp_tools/error/exceptions.py +204 -0
- dsp_tools/error/problems.py +10 -0
- dsp_tools/error/xmllib_errors.py +20 -0
- dsp_tools/error/xmllib_warnings.py +54 -0
- dsp_tools/error/xmllib_warnings_util.py +159 -0
- dsp_tools/error/xsd_validation_error_msg.py +19 -0
- dsp_tools/legacy_models/__init__.py +0 -0
- dsp_tools/{models → legacy_models}/datetimestamp.py +7 -7
- dsp_tools/{models → legacy_models}/langstring.py +1 -1
- dsp_tools/{models → legacy_models}/projectContext.py +4 -4
- dsp_tools/resources/schema/data.xsd +108 -83
- dsp_tools/resources/schema/lists-only.json +4 -23
- dsp_tools/resources/schema/project.json +80 -35
- dsp_tools/resources/schema/properties-only.json +1 -4
- dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
- dsp_tools/resources/start-stack/docker-compose.yml +34 -30
- dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
- dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
- dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
- dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
- dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
- dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
- dsp_tools/utils/ansi_colors.py +32 -0
- dsp_tools/utils/data_formats/__init__.py +0 -0
- dsp_tools/utils/{date_util.py → data_formats/date_util.py} +13 -1
- dsp_tools/utils/data_formats/iri_util.py +30 -0
- dsp_tools/utils/{shared.py → data_formats/shared.py} +1 -35
- dsp_tools/utils/{uri_util.py → data_formats/uri_util.py} +12 -2
- dsp_tools/utils/fuseki_bloating.py +63 -0
- dsp_tools/utils/json_parsing.py +22 -0
- dsp_tools/utils/rdf_constants.py +42 -0
- dsp_tools/utils/rdflib_utils.py +10 -0
- dsp_tools/utils/replace_id_with_iri.py +66 -0
- dsp_tools/utils/request_utils.py +238 -0
- dsp_tools/utils/xml_parsing/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
- dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
- dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
- dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
- dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
- dsp_tools/xmllib/CLAUDE.md +302 -0
- dsp_tools/xmllib/__init__.py +49 -0
- dsp_tools/xmllib/general_functions.py +877 -0
- dsp_tools/xmllib/internal/__init__.py +0 -0
- dsp_tools/xmllib/internal/checkers.py +162 -0
- dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
- dsp_tools/xmllib/internal/constants.py +46 -0
- dsp_tools/xmllib/internal/input_converters.py +155 -0
- dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
- dsp_tools/xmllib/internal/serialise_resource.py +177 -0
- dsp_tools/xmllib/internal/serialise_values.py +152 -0
- dsp_tools/xmllib/internal/type_aliases.py +11 -0
- dsp_tools/xmllib/models/config_options.py +28 -0
- dsp_tools/xmllib/models/date_formats.py +48 -0
- dsp_tools/xmllib/models/dsp_base_resources.py +1380 -400
- dsp_tools/xmllib/models/internal/__init__.py +0 -0
- dsp_tools/xmllib/models/internal/file_values.py +172 -0
- dsp_tools/xmllib/models/internal/geometry.py +162 -0
- dsp_tools/xmllib/models/{migration_metadata.py → internal/migration_metadata.py} +14 -10
- dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
- dsp_tools/xmllib/models/internal/values.py +342 -0
- dsp_tools/xmllib/models/licenses/__init__.py +0 -0
- dsp_tools/xmllib/models/licenses/other.py +59 -0
- dsp_tools/xmllib/models/licenses/recommended.py +107 -0
- dsp_tools/xmllib/models/permissions.py +41 -0
- dsp_tools/xmllib/models/res.py +1782 -0
- dsp_tools/xmllib/models/root.py +313 -26
- dsp_tools/xmllib/value_checkers.py +310 -47
- dsp_tools/xmllib/value_converters.py +765 -8
- dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
- dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
- dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
- {dsp_tools-9.1.0.post11.dist-info → dsp_tools-18.3.0.post13.dist-info}/entry_points.txt +1 -0
- dsp_tools/commands/project/create/project_create.py +0 -1107
- dsp_tools/commands/project/create/project_create_lists.py +0 -204
- dsp_tools/commands/project/create/project_validate.py +0 -453
- dsp_tools/commands/project/models/project_definition.py +0 -12
- dsp_tools/commands/rosetta.py +0 -124
- dsp_tools/commands/template.py +0 -30
- dsp_tools/commands/xml_validate/api_connection.py +0 -122
- dsp_tools/commands/xml_validate/deserialise_input.py +0 -135
- dsp_tools/commands/xml_validate/make_data_rdf.py +0 -193
- dsp_tools/commands/xml_validate/models/data_deserialised.py +0 -108
- dsp_tools/commands/xml_validate/models/data_rdf.py +0 -214
- dsp_tools/commands/xml_validate/models/input_problems.py +0 -191
- dsp_tools/commands/xml_validate/models/validation.py +0 -29
- dsp_tools/commands/xml_validate/reformat_validaton_result.py +0 -89
- dsp_tools/commands/xml_validate/sparql/construct_shapes.py +0 -16
- dsp_tools/commands/xml_validate/xml_validate.py +0 -151
- dsp_tools/commands/xmlupload/check_consistency_with_ontology.py +0 -253
- dsp_tools/commands/xmlupload/models/deserialise/deserialise_value.py +0 -236
- dsp_tools/commands/xmlupload/models/deserialise/xmlresource.py +0 -171
- dsp_tools/commands/xmlupload/models/namespace_context.py +0 -39
- dsp_tools/commands/xmlupload/models/ontology_lookup_models.py +0 -161
- dsp_tools/commands/xmlupload/models/ontology_problem_models.py +0 -178
- dsp_tools/commands/xmlupload/models/serialise/jsonld_serialiser.py +0 -40
- dsp_tools/commands/xmlupload/models/serialise/serialise_value.py +0 -51
- dsp_tools/commands/xmlupload/ontology_client.py +0 -92
- dsp_tools/commands/xmlupload/project_client.py +0 -91
- dsp_tools/commands/xmlupload/read_validate_xml_file.py +0 -99
- dsp_tools/models/custom_warnings.py +0 -31
- dsp_tools/models/exceptions.py +0 -90
- dsp_tools/resources/0100-template-repo/template.json +0 -45
- dsp_tools/resources/0100-template-repo/template.xml +0 -27
- dsp_tools/resources/start-stack/docker-compose-validation.yml +0 -5
- dsp_tools/resources/start-stack/start-stack-config.yml +0 -4
- dsp_tools/resources/xml_validate/api-shapes.ttl +0 -411
- dsp_tools/resources/xml_validate/replace_namespace.xslt +0 -61
- dsp_tools/utils/connection_live.py +0 -383
- dsp_tools/utils/iri_util.py +0 -14
- dsp_tools/utils/logger_config.py +0 -41
- dsp_tools/utils/set_encoder.py +0 -20
- dsp_tools/utils/xml_utils.py +0 -145
- dsp_tools/utils/xml_validation.py +0 -197
- dsp_tools/utils/xml_validation_models.py +0 -68
- dsp_tools/xmllib/models/file_values.py +0 -78
- dsp_tools/xmllib/models/resource.py +0 -415
- dsp_tools/xmllib/models/values.py +0 -428
- dsp_tools-9.1.0.post11.dist-info/METADATA +0 -130
- dsp_tools-9.1.0.post11.dist-info/RECORD +0 -167
- dsp_tools-9.1.0.post11.dist-info/WHEEL +0 -4
- dsp_tools-9.1.0.post11.dist-info/licenses/LICENSE +0 -674
- /dsp_tools/{commands/excel2json/new_lists → clients}/__init__.py +0 -0
- /dsp_tools/commands/{excel2json/new_lists/models → create}/__init__.py +0 -0
- /dsp_tools/commands/{project → create/create_on_server}/__init__.py +0 -0
- /dsp_tools/commands/{project/create → create/models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → create/parsing}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate → create/serialisation}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/models → excel2json/lists}/__init__.py +0 -0
- /dsp_tools/commands/{xml_validate/sparql → excel2json/lists/models}/__init__.py +0 -0
- /dsp_tools/commands/excel2json/{new_lists → lists}/models/deserialise.py +0 -0
- /dsp_tools/commands/{xmlupload/models/deserialise → get}/__init__.py +0 -0
- /dsp_tools/commands/{xmlupload/models/serialise → get/legacy_models}/__init__.py +0 -0
- /dsp_tools/commands/{project/models → get/legacy_models}/helpers.py +0 -0
- /dsp_tools/{models → commands/get/models}/__init__.py +0 -0
|
@@ -1,250 +1,518 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from collections.abc import Collection
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from dataclasses import field
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
from dsp_tools.
|
|
11
|
-
from dsp_tools.
|
|
12
|
-
from dsp_tools.xmllib.models.
|
|
13
|
-
from dsp_tools.xmllib.models.
|
|
14
|
-
from dsp_tools.xmllib.models.
|
|
15
|
-
from dsp_tools.xmllib.models.
|
|
16
|
-
from dsp_tools.xmllib.
|
|
17
|
-
from dsp_tools.xmllib.
|
|
7
|
+
from dsp_tools.error.xmllib_warnings import MessageInfo
|
|
8
|
+
from dsp_tools.error.xmllib_warnings_util import emit_xmllib_input_warning
|
|
9
|
+
from dsp_tools.xmllib.internal.input_converters import check_and_fix_collection_input
|
|
10
|
+
from dsp_tools.xmllib.internal.input_converters import check_and_fix_is_non_empty_string
|
|
11
|
+
from dsp_tools.xmllib.models.config_options import NewlineReplacement
|
|
12
|
+
from dsp_tools.xmllib.models.internal.geometry import Circle
|
|
13
|
+
from dsp_tools.xmllib.models.internal.geometry import GeometryPoint
|
|
14
|
+
from dsp_tools.xmllib.models.internal.geometry import GeometryShape
|
|
15
|
+
from dsp_tools.xmllib.models.internal.geometry import Polygon
|
|
16
|
+
from dsp_tools.xmllib.models.internal.geometry import Rectangle
|
|
17
|
+
from dsp_tools.xmllib.models.internal.geometry import Vector
|
|
18
|
+
from dsp_tools.xmllib.models.internal.migration_metadata import MigrationMetadata
|
|
19
|
+
from dsp_tools.xmllib.models.internal.values import LinkValue
|
|
20
|
+
from dsp_tools.xmllib.models.internal.values import Richtext
|
|
21
|
+
from dsp_tools.xmllib.models.internal.values import SimpleText
|
|
22
|
+
from dsp_tools.xmllib.models.internal.values import Value
|
|
23
|
+
from dsp_tools.xmllib.models.permissions import Permissions
|
|
18
24
|
from dsp_tools.xmllib.value_checkers import is_decimal
|
|
19
|
-
from dsp_tools.xmllib.value_checkers import
|
|
20
|
-
|
|
21
|
-
XML_NAMESPACE_MAP = {None: "https://dasch.swiss/schema", "xsi": "http://www.w3.org/2001/XMLSchema-instance"}
|
|
22
|
-
DASCH_SCHEMA = "{https://dasch.swiss/schema}"
|
|
25
|
+
from dsp_tools.xmllib.value_checkers import is_nonempty_value
|
|
23
26
|
|
|
24
27
|
LIST_SEPARATOR = "\n - "
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
@dataclass
|
|
28
|
-
class
|
|
31
|
+
class RegionResource:
|
|
29
32
|
res_id: str
|
|
30
33
|
label: str
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
permissions:
|
|
34
|
+
values: list[Value]
|
|
35
|
+
geometry: GeometryShape | None
|
|
36
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS
|
|
34
37
|
migration_metadata: MigrationMetadata | None = None
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
) ->
|
|
43
|
-
|
|
39
|
+
@staticmethod
|
|
40
|
+
def create_new(
|
|
41
|
+
res_id: str,
|
|
42
|
+
label: str,
|
|
43
|
+
region_of: str,
|
|
44
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
45
|
+
) -> RegionResource:
|
|
46
|
+
"""
|
|
47
|
+
Creates a new region resource.
|
|
48
|
+
A region is a region of interest (ROI) in a StillImageRepresentation.
|
|
49
|
+
|
|
50
|
+
Exactly one geometry shape has to be added to the resource with one of the following methods:
|
|
51
|
+
`add_rectangle`, `add_polygon`, `add_circle` (see documentation below for more information).
|
|
52
|
+
|
|
53
|
+
[See XML documentation for details](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#region)
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
res_id: ID of this region resource
|
|
57
|
+
label: label of this region resource
|
|
58
|
+
region_of: ID of the image resource that this region refers to (cardinality 1)
|
|
59
|
+
permissions: permissions of this region resource
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
A region resource
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
```python
|
|
66
|
+
region = xmllib.RegionResource.create_new(
|
|
67
|
+
res_id="ID",
|
|
68
|
+
label="label",
|
|
69
|
+
region_of="image_id",
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
"""
|
|
73
|
+
_check_strings(string_to_check=res_id, res_id=res_id, field_name="Resource ID")
|
|
74
|
+
res_id = str(res_id)
|
|
75
|
+
lbl = check_and_fix_is_non_empty_string(value=label, res_id=res_id, value_field="label")
|
|
76
|
+
return RegionResource(
|
|
44
77
|
res_id=res_id,
|
|
45
|
-
label=
|
|
46
|
-
|
|
47
|
-
|
|
78
|
+
label=lbl,
|
|
79
|
+
values=[
|
|
80
|
+
LinkValue.new(
|
|
81
|
+
value=region_of, prop_name="isRegionOf", resource_id=res_id, comment=None, permissions=permissions
|
|
82
|
+
)
|
|
83
|
+
],
|
|
84
|
+
geometry=None,
|
|
48
85
|
permissions=permissions,
|
|
49
86
|
)
|
|
50
87
|
|
|
51
|
-
def
|
|
52
|
-
self
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
88
|
+
def add_rectangle(
|
|
89
|
+
self,
|
|
90
|
+
point1: tuple[float, float],
|
|
91
|
+
point2: tuple[float, float],
|
|
92
|
+
line_width: float = 2,
|
|
93
|
+
color: str = "#5b24bf",
|
|
94
|
+
active: bool = True,
|
|
95
|
+
) -> RegionResource:
|
|
96
|
+
"""
|
|
97
|
+
Add a rectangle shape to the region.
|
|
98
|
+
|
|
99
|
+
[For a visual example see the XML documentation](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#geometry)
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
point1: first point of the rectangle represented as two numbers between 0 and 1 in the format (x, y)
|
|
103
|
+
point2: second point of the rectangle represented as two numbers between 0 and 1 in the format (x, y)
|
|
104
|
+
line_width: A number in pixels between 1 - 5
|
|
105
|
+
color: A hexadecimal color value which starts with a `#` followed by 3 or 6 numerals.
|
|
106
|
+
The default value was chosen as it is distinguishable for most color-blind people.
|
|
107
|
+
active: If set to `False`, the region is marked as 'deleted'
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Region with added rectangle
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
```python
|
|
114
|
+
region = region.add_rectangle(
|
|
115
|
+
point1=(0.1, 0.1),
|
|
116
|
+
point2=(0.2, 0.2),
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# with custom display values
|
|
122
|
+
region = region.add_rectangle(
|
|
123
|
+
point1=(0.1, 0.1),
|
|
124
|
+
point2=(0.2, 0.2),
|
|
125
|
+
line_width=3,
|
|
126
|
+
color="#32a873",
|
|
66
127
|
)
|
|
67
|
-
|
|
128
|
+
```
|
|
129
|
+
"""
|
|
130
|
+
self.geometry = Rectangle(
|
|
131
|
+
point1=GeometryPoint(point1[0], point1[1], self.res_id),
|
|
132
|
+
point2=GeometryPoint(point2[0], point2[1], self.res_id),
|
|
133
|
+
line_width=line_width,
|
|
134
|
+
color=color,
|
|
135
|
+
active=active,
|
|
136
|
+
resource_id=self.res_id,
|
|
137
|
+
)
|
|
68
138
|
return self
|
|
69
139
|
|
|
70
|
-
def
|
|
71
|
-
self
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
140
|
+
def add_polygon(
|
|
141
|
+
self,
|
|
142
|
+
points: list[tuple[float, float]],
|
|
143
|
+
line_width: float = 2,
|
|
144
|
+
color: str = "#5b24bf",
|
|
145
|
+
active: bool = True,
|
|
146
|
+
) -> RegionResource:
|
|
147
|
+
"""
|
|
148
|
+
Add a polygon shape to the region.
|
|
149
|
+
A polygon should have at least three points.
|
|
150
|
+
If you wish to create a rectangle, please use the designated `add_rectangle` method.
|
|
76
151
|
|
|
77
|
-
|
|
78
|
-
attribs = {"label": self.label, "id": self.res_id}
|
|
79
|
-
if self.permissions:
|
|
80
|
-
attribs["permissions"] = self.permissions
|
|
81
|
-
return etree.Element(f"{DASCH_SCHEMA}annotation", attrib=attribs, nsmap=XML_NAMESPACE_MAP)
|
|
152
|
+
[For a visual example see the XML documentation](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#geometry)
|
|
82
153
|
|
|
83
|
-
|
|
84
|
-
return LinkValue(value=self.annotation_of, prop_name="isAnnotationOf", resource_id=self.res_id).serialise()
|
|
154
|
+
**Please note that this cannot currently be displayed in the dsp-app.**
|
|
85
155
|
|
|
156
|
+
Args:
|
|
157
|
+
points: list of tuples containing two numbers between 0 and 1 in the format (x, y)
|
|
158
|
+
line_width: A number in pixels between 1 - 5
|
|
159
|
+
color: A hexadecimal color value which starts with a `#` followed by 3 or 6 numerals.
|
|
160
|
+
The default value was chosen as it is distinguishable for most color-blind people.
|
|
161
|
+
active: If set to `False` the region is marked as 'deleted'
|
|
86
162
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
res_id: str
|
|
90
|
-
label: str
|
|
91
|
-
color: str
|
|
92
|
-
region_of: str
|
|
93
|
-
geometry: dict[str, Any]
|
|
94
|
-
comments: list[str]
|
|
95
|
-
permissions: str = "res-default"
|
|
96
|
-
migration_metadata: MigrationMetadata | None = None
|
|
163
|
+
Returns:
|
|
164
|
+
Region with added polygon
|
|
97
165
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
166
|
+
Examples:
|
|
167
|
+
```python
|
|
168
|
+
region = region.add_polygon(
|
|
169
|
+
points=[(0.1, 0.1), (0.2, 0.2), (0.3, 0.3)],
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# with custom display values
|
|
175
|
+
region = region.add_polygon(
|
|
176
|
+
points=[(0.1, 0.1), (0.2, 0.2), (0.3, 0.3)],
|
|
177
|
+
line_width=3,
|
|
178
|
+
color="#32a873",
|
|
105
179
|
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
180
|
+
```
|
|
181
|
+
"""
|
|
182
|
+
geom_points = [GeometryPoint(val[0], val[1], self.res_id) for val in points]
|
|
183
|
+
self.geometry = Polygon(
|
|
184
|
+
points=geom_points, line_width=line_width, color=color, active=active, resource_id=self.res_id
|
|
185
|
+
)
|
|
186
|
+
return self
|
|
110
187
|
|
|
111
|
-
def
|
|
188
|
+
def add_circle(
|
|
112
189
|
self,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
comments: list[str],
|
|
119
|
-
permissions: str = "res-default",
|
|
190
|
+
center: tuple[float, float],
|
|
191
|
+
radius: tuple[float, float],
|
|
192
|
+
line_width: float = 2,
|
|
193
|
+
color: str = "#5b24bf",
|
|
194
|
+
active: bool = True,
|
|
120
195
|
) -> RegionResource:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
196
|
+
"""
|
|
197
|
+
Add a circle shape to the region.
|
|
198
|
+
|
|
199
|
+
[For a visual example see the XML documentation](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#geometry)
|
|
200
|
+
|
|
201
|
+
**Please note that this cannot currently be displayed in the dsp-app.**
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
center: center of the circle, represented as two numbers between 0 and 1 in the format (x, y)
|
|
205
|
+
radius: radius of the circle, represented as a 2-dimensional vector,
|
|
206
|
+
i.e. two numbers between 0 and 1 in the format (x, y)
|
|
207
|
+
line_width: A number in pixels between 1 - 5
|
|
208
|
+
color: A hexadecimal color value which starts with a `#` followed by 3 or 6 numerals.
|
|
209
|
+
The default value was chosen as it is distinguishable for most color-blind people.
|
|
210
|
+
active: If set to `False` the region is marked as 'deleted'
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Region with added circle
|
|
214
|
+
|
|
215
|
+
Examples:
|
|
216
|
+
```python
|
|
217
|
+
region = region.add_circle(
|
|
218
|
+
center=(0.1, 0.1),
|
|
219
|
+
radius=(0.2, 0.2),
|
|
220
|
+
)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
# with custom display values
|
|
225
|
+
region = region.add_circle(
|
|
226
|
+
center=(0.1, 0.1),
|
|
227
|
+
radius=(0.2, 0.2),
|
|
228
|
+
line_width=3,
|
|
229
|
+
color="#32a873",
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
"""
|
|
233
|
+
self.geometry = Circle(
|
|
234
|
+
center=GeometryPoint(center[0], center[1], self.res_id),
|
|
235
|
+
radius=Vector(radius[0], radius[1], self.res_id),
|
|
236
|
+
line_width=line_width,
|
|
124
237
|
color=color,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
comments=comments,
|
|
128
|
-
permissions=permissions,
|
|
238
|
+
active=active,
|
|
239
|
+
resource_id=self.res_id,
|
|
129
240
|
)
|
|
130
|
-
|
|
131
|
-
def add_comment(self, comment: str) -> RegionResource:
|
|
132
|
-
self.comments.append(comment)
|
|
133
241
|
return self
|
|
134
242
|
|
|
135
|
-
def
|
|
136
|
-
self
|
|
243
|
+
def add_comment(
|
|
244
|
+
self,
|
|
245
|
+
text: str,
|
|
246
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
247
|
+
comment: str | None = None,
|
|
248
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
249
|
+
) -> RegionResource:
|
|
250
|
+
"""
|
|
251
|
+
Add a comment to the region
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
text: text of the comment
|
|
255
|
+
permissions: optional permissions of this value
|
|
256
|
+
comment: optional comment about this comment
|
|
257
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
The original region, with the added comment
|
|
261
|
+
|
|
262
|
+
Examples:
|
|
263
|
+
```python
|
|
264
|
+
region = region.add_comment("comment text")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
region = region.add_comment(text="comment text", comment="Comment about the comment.")
|
|
269
|
+
```
|
|
270
|
+
"""
|
|
271
|
+
self.values.append(
|
|
272
|
+
Richtext.new(
|
|
273
|
+
value=text,
|
|
274
|
+
prop_name="hasComment",
|
|
275
|
+
permissions=permissions,
|
|
276
|
+
comment=comment,
|
|
277
|
+
resource_id=self.res_id,
|
|
278
|
+
newline_replacement=newline_replacement,
|
|
279
|
+
)
|
|
280
|
+
)
|
|
137
281
|
return self
|
|
138
282
|
|
|
139
|
-
def
|
|
140
|
-
self,
|
|
283
|
+
def add_comment_multiple(
|
|
284
|
+
self,
|
|
285
|
+
texts: Collection[str],
|
|
286
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
287
|
+
comment: str | None = None,
|
|
288
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
141
289
|
) -> RegionResource:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
290
|
+
"""
|
|
291
|
+
Add several comments to the region
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
texts: list of texts
|
|
295
|
+
permissions: optional permissions of these values
|
|
296
|
+
comment: optional comment about these comments
|
|
297
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
The original region, with the added comments
|
|
301
|
+
|
|
302
|
+
Examples:
|
|
303
|
+
```python
|
|
304
|
+
region = region.add_comment_multiple(["comment 1", "comment 2"])
|
|
305
|
+
```
|
|
306
|
+
"""
|
|
307
|
+
vals = check_and_fix_collection_input(texts, "hasComment", self.res_id)
|
|
308
|
+
comnts = [
|
|
309
|
+
Richtext.new(
|
|
310
|
+
value=x,
|
|
311
|
+
prop_name="hasComment",
|
|
312
|
+
permissions=permissions,
|
|
313
|
+
comment=comment,
|
|
314
|
+
resource_id=self.res_id,
|
|
315
|
+
newline_replacement=newline_replacement,
|
|
146
316
|
)
|
|
147
|
-
|
|
148
|
-
return self
|
|
149
|
-
|
|
150
|
-
def serialise(self) -> etree._Element:
|
|
151
|
-
self.comments = _transform_unexpected_input(self.comments, "comments", self.res_id)
|
|
152
|
-
res_ele = self._serialise_resource_element()
|
|
153
|
-
res_ele.append(self._serialise_geometry())
|
|
154
|
-
res_ele.extend(self._serialise_values())
|
|
155
|
-
if self.comments:
|
|
156
|
-
res_ele.append(_serialise_has_comment(self.comments, self.res_id))
|
|
157
|
-
return res_ele
|
|
158
|
-
|
|
159
|
-
def _serialise_resource_element(self) -> etree._Element:
|
|
160
|
-
attribs = {"label": self.label, "id": self.res_id}
|
|
161
|
-
if self.permissions:
|
|
162
|
-
attribs["permissions"] = self.permissions
|
|
163
|
-
return etree.Element(f"{DASCH_SCHEMA}region", attrib=attribs, nsmap=XML_NAMESPACE_MAP)
|
|
164
|
-
|
|
165
|
-
def _serialise_values(self) -> list[etree._Element]:
|
|
166
|
-
return [
|
|
167
|
-
ColorValue(value=self.color, prop_name="hasColor", resource_id=self.res_id).serialise(),
|
|
168
|
-
LinkValue(value=self.region_of, prop_name="isRegionOf", resource_id=self.res_id).serialise(),
|
|
317
|
+
for x in vals
|
|
169
318
|
]
|
|
319
|
+
self.values.extend(comnts)
|
|
320
|
+
return self
|
|
170
321
|
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
322
|
+
def add_comment_optional(
|
|
323
|
+
self,
|
|
324
|
+
text: Any,
|
|
325
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
326
|
+
comment: str | None = None,
|
|
327
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
328
|
+
) -> RegionResource:
|
|
329
|
+
"""
|
|
330
|
+
If the value is not empty, add it as comment, otherwise return the region unchanged.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
text: text of the comment (or empty value)
|
|
334
|
+
permissions: optional permissions of this value
|
|
335
|
+
comment: optional comment about this comment
|
|
336
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
The original region, with the added comment
|
|
340
|
+
|
|
341
|
+
Examples:
|
|
342
|
+
```python
|
|
343
|
+
region = region.add_comment_optional("comment text")
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
region = region.add_comment_optional(None)
|
|
348
|
+
```
|
|
349
|
+
"""
|
|
350
|
+
if is_nonempty_value(text):
|
|
351
|
+
return self.add_comment(text, permissions, comment, newline_replacement)
|
|
352
|
+
return self
|
|
177
353
|
|
|
178
354
|
|
|
179
355
|
@dataclass
|
|
180
356
|
class LinkResource:
|
|
181
357
|
res_id: str
|
|
182
358
|
label: str
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
permissions: str = "res-default"
|
|
359
|
+
values: list[Value]
|
|
360
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS
|
|
186
361
|
migration_metadata: MigrationMetadata | None = None
|
|
187
362
|
|
|
188
|
-
|
|
189
|
-
|
|
363
|
+
@staticmethod
|
|
364
|
+
def create_new(
|
|
365
|
+
res_id: str,
|
|
366
|
+
label: str,
|
|
367
|
+
link_to: Collection[str],
|
|
368
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
190
369
|
) -> LinkResource:
|
|
370
|
+
"""
|
|
371
|
+
Creates a new link resource.
|
|
372
|
+
A link resource is like a container that groups together several other resources of different classes.
|
|
373
|
+
|
|
374
|
+
[See XML documentation for details](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#link)
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
res_id: ID of this link resource
|
|
378
|
+
label: label of this link resource
|
|
379
|
+
link_to: IDs of the resources that should be linked together (cardinality 1-n)
|
|
380
|
+
permissions: permissions of this link resource
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
A link resource
|
|
384
|
+
|
|
385
|
+
Examples:
|
|
386
|
+
```python
|
|
387
|
+
link_resource = xmllib.LinkResource.create_new(
|
|
388
|
+
res_id="ID",
|
|
389
|
+
label="label",
|
|
390
|
+
link_to=["target_resource_id_1", "target_resource_id_2"],
|
|
391
|
+
)
|
|
392
|
+
```
|
|
393
|
+
"""
|
|
394
|
+
_check_strings(string_to_check=res_id, res_id=res_id, field_name="Resource ID")
|
|
395
|
+
res_id = str(res_id)
|
|
396
|
+
lbl = check_and_fix_is_non_empty_string(value=label, res_id=res_id, value_field="label")
|
|
397
|
+
links_to = check_and_fix_collection_input(link_to, "hasLinkTo", res_id)
|
|
398
|
+
link_vals: list[Value] = [
|
|
399
|
+
LinkValue.new(value=x, prop_name="hasLinkTo", resource_id=res_id, comment=None, permissions=permissions)
|
|
400
|
+
for x in links_to
|
|
401
|
+
]
|
|
191
402
|
return LinkResource(
|
|
192
403
|
res_id=res_id,
|
|
193
|
-
label=
|
|
194
|
-
|
|
195
|
-
comments=comments,
|
|
404
|
+
label=lbl,
|
|
405
|
+
values=link_vals,
|
|
196
406
|
permissions=permissions,
|
|
197
407
|
)
|
|
198
408
|
|
|
199
|
-
def add_comment(
|
|
200
|
-
self
|
|
409
|
+
def add_comment(
|
|
410
|
+
self,
|
|
411
|
+
text: str,
|
|
412
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
413
|
+
comment: str | None = None,
|
|
414
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
415
|
+
) -> LinkResource:
|
|
416
|
+
"""
|
|
417
|
+
Add a comment to the resource
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
text: text of the comment
|
|
421
|
+
permissions: optional permissions of this value
|
|
422
|
+
comment: optional comment about this comment
|
|
423
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
The original resource, with the added comment
|
|
427
|
+
|
|
428
|
+
Examples:
|
|
429
|
+
```python
|
|
430
|
+
link_resource = link_resource.add_comment("comment text")
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
link_resource = link_resource.add_comment(text="comment text", comment="Comment about the comment.")
|
|
435
|
+
```
|
|
436
|
+
"""
|
|
437
|
+
self.values.append(
|
|
438
|
+
Richtext.new(
|
|
439
|
+
value=text,
|
|
440
|
+
prop_name="hasComment",
|
|
441
|
+
permissions=permissions,
|
|
442
|
+
comment=comment,
|
|
443
|
+
resource_id=self.res_id,
|
|
444
|
+
newline_replacement=newline_replacement,
|
|
445
|
+
)
|
|
446
|
+
)
|
|
201
447
|
return self
|
|
202
448
|
|
|
203
|
-
def
|
|
204
|
-
self
|
|
449
|
+
def add_comment_multiple(
|
|
450
|
+
self,
|
|
451
|
+
texts: Collection[str],
|
|
452
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
453
|
+
comment: str | None = None,
|
|
454
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
455
|
+
) -> LinkResource:
|
|
456
|
+
"""
|
|
457
|
+
Add several comments to the resource
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
texts: list of texts
|
|
461
|
+
permissions: optional permissions of these values
|
|
462
|
+
comment: optional comment about this comment
|
|
463
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
The original resource, with the added comments
|
|
467
|
+
|
|
468
|
+
Examples:
|
|
469
|
+
```python
|
|
470
|
+
link_resource = link_resource.add_comment_multiple(["comment 1", "comment 2"])
|
|
471
|
+
```
|
|
472
|
+
"""
|
|
473
|
+
vals = check_and_fix_collection_input(texts, "hasComment", self.res_id)
|
|
474
|
+
for v in vals:
|
|
475
|
+
self.add_comment(v, permissions, comment, newline_replacement)
|
|
205
476
|
return self
|
|
206
477
|
|
|
207
|
-
def
|
|
208
|
-
self,
|
|
478
|
+
def add_comment_optional(
|
|
479
|
+
self,
|
|
480
|
+
text: Any,
|
|
481
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
482
|
+
comment: str | None = None,
|
|
483
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
209
484
|
) -> LinkResource:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
215
|
-
|
|
485
|
+
"""
|
|
486
|
+
If the value is not empty, add it as comment, otherwise return the resource unchanged.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
text: text of the comment (or empty value)
|
|
490
|
+
permissions: optional permissions of this value
|
|
491
|
+
comment: optional comment about this comment
|
|
492
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
The original resource, with the added comment
|
|
496
|
+
|
|
497
|
+
Examples:
|
|
498
|
+
```python
|
|
499
|
+
link_resource = link_resource.add_comment_optional("comment text")
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
link_resource = link_resource.add_comment_optional(None)
|
|
504
|
+
```
|
|
505
|
+
"""
|
|
506
|
+
if is_nonempty_value(text):
|
|
507
|
+
return self.add_comment(text, permissions, comment, newline_replacement)
|
|
216
508
|
return self
|
|
217
509
|
|
|
218
|
-
def serialise(self) -> etree._Element:
|
|
219
|
-
self._check_for_and_convert_unexpected_input()
|
|
220
|
-
res_ele = self._serialise_resource_element()
|
|
221
|
-
res_ele.append(_serialise_has_comment(self.comments, self.res_id))
|
|
222
|
-
res_ele.append(self._serialise_links())
|
|
223
|
-
return res_ele
|
|
224
|
-
|
|
225
|
-
def _check_for_and_convert_unexpected_input(self) -> None:
|
|
226
|
-
_check_strings(string_to_check=self.res_id, res_id=self.res_id, field_name="Resource ID")
|
|
227
|
-
_check_strings(string_to_check=self.label, res_id=self.res_id, field_name="Label")
|
|
228
|
-
self.comments = _transform_unexpected_input(self.comments, "comments", self.res_id)
|
|
229
|
-
self.link_to = _transform_unexpected_input(self.link_to, "link_to", self.res_id)
|
|
230
|
-
|
|
231
|
-
def _serialise_resource_element(self) -> etree._Element:
|
|
232
|
-
attribs = {"label": self.label, "id": self.res_id}
|
|
233
|
-
if self.permissions:
|
|
234
|
-
attribs["permissions"] = self.permissions
|
|
235
|
-
return etree.Element(f"{DASCH_SCHEMA}link", attrib=attribs, nsmap=XML_NAMESPACE_MAP)
|
|
236
|
-
|
|
237
|
-
def _serialise_links(self) -> etree._Element:
|
|
238
|
-
prop_ele = etree.Element(f"{DASCH_SCHEMA}resptr-prop", name="hasLinkTo", nsmap=XML_NAMESPACE_MAP)
|
|
239
|
-
vals = [LinkValue(value=x, prop_name="hasLinkTo", resource_id=self.res_id) for x in self.link_to]
|
|
240
|
-
prop_ele.extend([v.make_element() for v in vals])
|
|
241
|
-
return prop_ele
|
|
242
|
-
|
|
243
510
|
|
|
244
511
|
@dataclass
|
|
245
512
|
class SegmentBounds:
|
|
246
513
|
segment_start: float | int | str
|
|
247
514
|
segment_end: float | int | str
|
|
515
|
+
permissions: Permissions
|
|
248
516
|
res_id: str
|
|
249
517
|
|
|
250
518
|
def __post_init__(self) -> None:
|
|
@@ -254,309 +522,1021 @@ class SegmentBounds:
|
|
|
254
522
|
if not is_decimal(self.segment_end):
|
|
255
523
|
msg.append(f"Segment End Value: {self.segment_end} | Type: {type(self.segment_start)}")
|
|
256
524
|
if msg:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
f"The following places have an unexpected type:"
|
|
525
|
+
wrng = f"{LIST_SEPARATOR}{LIST_SEPARATOR.join(msg)}"
|
|
526
|
+
msg_info = MessageInfo(
|
|
527
|
+
f"Segment bounds must be a float or integer. The following places have an unexpected type: {wrng}",
|
|
528
|
+
self.res_id,
|
|
260
529
|
)
|
|
261
|
-
|
|
262
|
-
|
|
530
|
+
emit_xmllib_input_warning(msg_info)
|
|
531
|
+
self.segment_start = str(self.segment_start)
|
|
532
|
+
self.segment_end = str(self.segment_end)
|
|
263
533
|
|
|
264
534
|
|
|
265
535
|
@dataclass
|
|
266
536
|
class VideoSegmentResource:
|
|
267
537
|
res_id: str
|
|
268
538
|
label: str
|
|
269
|
-
segment_of: str
|
|
270
539
|
segment_bounds: SegmentBounds
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
descriptions: list[str] = field(default_factory=list)
|
|
274
|
-
keywords: list[str] = field(default_factory=list)
|
|
275
|
-
relates_to: list[str] = field(default_factory=list)
|
|
276
|
-
permissions: str = "res-default"
|
|
540
|
+
values: list[Value]
|
|
541
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS
|
|
277
542
|
migration_metadata: MigrationMetadata | None = None
|
|
278
543
|
|
|
279
|
-
|
|
280
|
-
|
|
544
|
+
@staticmethod
|
|
545
|
+
def create_new(
|
|
281
546
|
res_id: str,
|
|
282
547
|
label: str,
|
|
283
548
|
segment_of: str,
|
|
284
|
-
segment_start: float,
|
|
285
|
-
segment_end: float,
|
|
286
|
-
|
|
287
|
-
permissions: str = "res-default",
|
|
549
|
+
segment_start: float | int | str,
|
|
550
|
+
segment_end: float | int | str,
|
|
551
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
288
552
|
) -> VideoSegmentResource:
|
|
553
|
+
"""
|
|
554
|
+
Creates a new video segment resource, i.e. a time span of a MovingImageRepresentation.
|
|
555
|
+
|
|
556
|
+
[See XML documentation for details](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#video-segment-and-audio-segment)
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
res_id: ID of this video segment resource
|
|
560
|
+
label: label of this video segment resource
|
|
561
|
+
segment_of: ID of the video resource that this segment refers to (cardinality 1)
|
|
562
|
+
segment_start: start of the segment in seconds (cardinality 1)
|
|
563
|
+
segment_end: end of the segment in seconds (cardinality 1)
|
|
564
|
+
permissions: permissions of this resource
|
|
565
|
+
|
|
566
|
+
Returns:
|
|
567
|
+
A video segment resource
|
|
568
|
+
|
|
569
|
+
Examples:
|
|
570
|
+
```python
|
|
571
|
+
video_segment = xmllib.VideoSegmentResource.create_new(
|
|
572
|
+
res_id="ID",
|
|
573
|
+
label="label",
|
|
574
|
+
segment_of="video_resource_id",
|
|
575
|
+
segment_start=1.2,
|
|
576
|
+
segment_end=3.4,
|
|
577
|
+
)
|
|
578
|
+
```
|
|
579
|
+
"""
|
|
580
|
+
_check_strings(string_to_check=res_id, res_id=res_id, field_name="Resource ID")
|
|
581
|
+
res_id = str(res_id)
|
|
582
|
+
lbl = check_and_fix_is_non_empty_string(value=label, res_id=res_id, value_field="label")
|
|
583
|
+
segment_of_val = LinkValue.new(
|
|
584
|
+
value=segment_of, prop_name="isSegmentOf", permissions=permissions, comment=None, resource_id=res_id
|
|
585
|
+
)
|
|
289
586
|
return VideoSegmentResource(
|
|
290
587
|
res_id=res_id,
|
|
291
|
-
label=
|
|
292
|
-
|
|
293
|
-
segment_bounds=SegmentBounds(segment_start, segment_end, res_id),
|
|
294
|
-
title=title,
|
|
588
|
+
label=lbl,
|
|
589
|
+
values=[segment_of_val],
|
|
590
|
+
segment_bounds=SegmentBounds(segment_start, segment_end, permissions, res_id),
|
|
295
591
|
permissions=permissions,
|
|
296
592
|
)
|
|
297
593
|
|
|
298
|
-
def add_title(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
594
|
+
def add_title(
|
|
595
|
+
self,
|
|
596
|
+
title: str,
|
|
597
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
598
|
+
comment: str | None = None,
|
|
599
|
+
) -> VideoSegmentResource:
|
|
600
|
+
"""
|
|
601
|
+
Add a title to the resource.
|
|
602
|
+
|
|
603
|
+
Args:
|
|
604
|
+
title: text
|
|
605
|
+
permissions: permissions of the value
|
|
606
|
+
comment: comments on the value
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
The original resource, with the added title
|
|
610
|
+
|
|
611
|
+
Examples:
|
|
612
|
+
```python
|
|
613
|
+
video_segment = video_segment.add_title("segment title")
|
|
614
|
+
```
|
|
615
|
+
"""
|
|
616
|
+
existing_titles = [x for x in self.values if x.prop_name == "hasTitle"]
|
|
617
|
+
if any(existing_titles):
|
|
618
|
+
_warn_value_exists(
|
|
619
|
+
old_value=existing_titles.pop(0).value, new_value=title, value_field="title", res_id=self.res_id
|
|
620
|
+
)
|
|
621
|
+
self.values.append(
|
|
622
|
+
SimpleText.new(
|
|
623
|
+
value=title,
|
|
624
|
+
prop_name="hasTitle",
|
|
625
|
+
permissions=permissions,
|
|
626
|
+
comment=comment,
|
|
627
|
+
resource_id=self.res_id,
|
|
628
|
+
)
|
|
629
|
+
)
|
|
302
630
|
return self
|
|
303
631
|
|
|
304
|
-
def
|
|
305
|
-
self
|
|
632
|
+
def add_title_optional(
|
|
633
|
+
self,
|
|
634
|
+
title: Any,
|
|
635
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
636
|
+
comment: str | None = None,
|
|
637
|
+
) -> VideoSegmentResource:
|
|
638
|
+
"""
|
|
639
|
+
If the value is not empty, add it as title, otherwise return the resource unchanged.
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
title: text or empty value
|
|
643
|
+
permissions: permissions of the value
|
|
644
|
+
comment: comments on the value
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
The original resource, with the added title
|
|
648
|
+
|
|
649
|
+
Examples:
|
|
650
|
+
```python
|
|
651
|
+
video_segment = video_segment.add_title("segment title")
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
```python
|
|
655
|
+
video_segment = video_segment.add_title(None)
|
|
656
|
+
```
|
|
657
|
+
"""
|
|
658
|
+
if is_nonempty_value(title):
|
|
659
|
+
self.add_title(title, permissions, comment)
|
|
306
660
|
return self
|
|
307
661
|
|
|
308
|
-
def
|
|
309
|
-
self
|
|
662
|
+
def add_comment(
|
|
663
|
+
self,
|
|
664
|
+
text: str,
|
|
665
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
666
|
+
comment: str | None = None,
|
|
667
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
668
|
+
) -> VideoSegmentResource:
|
|
669
|
+
"""
|
|
670
|
+
Add a comment to the resource
|
|
671
|
+
|
|
672
|
+
Args:
|
|
673
|
+
text: text
|
|
674
|
+
permissions: optional permissions of this value
|
|
675
|
+
comment: optional comment about this comment
|
|
676
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
677
|
+
|
|
678
|
+
Returns:
|
|
679
|
+
The original resource, with the added comment
|
|
680
|
+
|
|
681
|
+
Examples:
|
|
682
|
+
```python
|
|
683
|
+
video_segment = video_segment.add_comment("comment text")
|
|
684
|
+
```
|
|
685
|
+
"""
|
|
686
|
+
self.values.append(
|
|
687
|
+
Richtext.new(
|
|
688
|
+
value=text,
|
|
689
|
+
prop_name="hasComment",
|
|
690
|
+
permissions=permissions,
|
|
691
|
+
comment=comment,
|
|
692
|
+
resource_id=self.res_id,
|
|
693
|
+
newline_replacement=newline_replacement,
|
|
694
|
+
)
|
|
695
|
+
)
|
|
310
696
|
return self
|
|
311
697
|
|
|
312
|
-
def
|
|
313
|
-
self
|
|
698
|
+
def add_comment_multiple(
|
|
699
|
+
self,
|
|
700
|
+
texts: Collection[str],
|
|
701
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
702
|
+
comment: str | None = None,
|
|
703
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
704
|
+
) -> VideoSegmentResource:
|
|
705
|
+
"""
|
|
706
|
+
Add several comments to the resource
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
texts: list of texts
|
|
710
|
+
permissions: optional permissions of these values
|
|
711
|
+
comment: optional comment about these comments
|
|
712
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
713
|
+
|
|
714
|
+
Returns:
|
|
715
|
+
The original resource, with the added comments
|
|
716
|
+
|
|
717
|
+
Examples:
|
|
718
|
+
```python
|
|
719
|
+
video_segment = video_segment.add_comment_multiple(["comment 1", "comment 2"])
|
|
720
|
+
```
|
|
721
|
+
"""
|
|
722
|
+
vals = check_and_fix_collection_input(texts, "hasComment", self.res_id)
|
|
723
|
+
for v in vals:
|
|
724
|
+
self.add_comment(v, permissions, comment, newline_replacement)
|
|
314
725
|
return self
|
|
315
726
|
|
|
316
|
-
def
|
|
317
|
-
self
|
|
727
|
+
def add_comment_optional(
|
|
728
|
+
self,
|
|
729
|
+
text: Any,
|
|
730
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
731
|
+
comment: str | None = None,
|
|
732
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
733
|
+
) -> VideoSegmentResource:
|
|
734
|
+
"""
|
|
735
|
+
If the value is not empty, add it as comment, otherwise return the resource unchanged.
|
|
736
|
+
|
|
737
|
+
Args:
|
|
738
|
+
text: text of the comment (or empty value)
|
|
739
|
+
permissions: optional permissions of this value
|
|
740
|
+
comment: optional comment about this comment
|
|
741
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
The original resource, with the added comment
|
|
745
|
+
|
|
746
|
+
Examples:
|
|
747
|
+
```python
|
|
748
|
+
video_segment = video_segment.add_comment_optional("comment text")
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
```python
|
|
752
|
+
video_segment = video_segment.add_comment_optional(None)
|
|
753
|
+
```
|
|
754
|
+
"""
|
|
755
|
+
if is_nonempty_value(text):
|
|
756
|
+
self.add_comment(text, permissions, comment, newline_replacement)
|
|
318
757
|
return self
|
|
319
758
|
|
|
320
|
-
def
|
|
321
|
-
self
|
|
759
|
+
def add_description(
|
|
760
|
+
self,
|
|
761
|
+
description: str,
|
|
762
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
763
|
+
comment: str | None = None,
|
|
764
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
765
|
+
) -> VideoSegmentResource:
|
|
766
|
+
"""
|
|
767
|
+
Add a description to the resource
|
|
768
|
+
|
|
769
|
+
Args:
|
|
770
|
+
description: text
|
|
771
|
+
permissions: optional permissions of this value
|
|
772
|
+
comment: optional comment
|
|
773
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
774
|
+
|
|
775
|
+
Returns:
|
|
776
|
+
The original resource, with the added description
|
|
777
|
+
|
|
778
|
+
Examples:
|
|
779
|
+
```python
|
|
780
|
+
video_segment = video_segment.add_description("description text")
|
|
781
|
+
```
|
|
782
|
+
"""
|
|
783
|
+
self.values.append(
|
|
784
|
+
Richtext.new(
|
|
785
|
+
value=description,
|
|
786
|
+
prop_name="hasDescription",
|
|
787
|
+
permissions=permissions,
|
|
788
|
+
comment=comment,
|
|
789
|
+
resource_id=self.res_id,
|
|
790
|
+
newline_replacement=newline_replacement,
|
|
791
|
+
)
|
|
792
|
+
)
|
|
322
793
|
return self
|
|
323
794
|
|
|
324
|
-
def
|
|
325
|
-
self
|
|
795
|
+
def add_description_multiple(
|
|
796
|
+
self,
|
|
797
|
+
descriptions: Collection[str],
|
|
798
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
799
|
+
comment: str | None = None,
|
|
800
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
801
|
+
) -> VideoSegmentResource:
|
|
802
|
+
"""
|
|
803
|
+
Add several descriptions to the resource
|
|
804
|
+
|
|
805
|
+
Args:
|
|
806
|
+
descriptions: list of texts
|
|
807
|
+
permissions: optional permissions of these value
|
|
808
|
+
comment: optional comment
|
|
809
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
810
|
+
|
|
811
|
+
Returns:
|
|
812
|
+
The original resource, with the added descriptions
|
|
813
|
+
|
|
814
|
+
Examples:
|
|
815
|
+
```python
|
|
816
|
+
video_segment = video_segment.add_description_multiple(["description 1", "description 2"])
|
|
817
|
+
```
|
|
818
|
+
"""
|
|
819
|
+
vals = check_and_fix_collection_input(descriptions, "description", self.res_id)
|
|
820
|
+
for v in vals:
|
|
821
|
+
self.add_description(v, permissions, comment, newline_replacement)
|
|
326
822
|
return self
|
|
327
823
|
|
|
328
|
-
def
|
|
329
|
-
self
|
|
824
|
+
def add_description_optional(
|
|
825
|
+
self,
|
|
826
|
+
description: Any,
|
|
827
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
828
|
+
comment: str | None = None,
|
|
829
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
830
|
+
) -> VideoSegmentResource:
|
|
831
|
+
"""
|
|
832
|
+
If the value is not empty, add it as description, otherwise return the resource unchanged.
|
|
833
|
+
|
|
834
|
+
Args:
|
|
835
|
+
description: text or empty value
|
|
836
|
+
permissions: optional permissions of this value
|
|
837
|
+
comment: optional comment
|
|
838
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
839
|
+
|
|
840
|
+
Returns:
|
|
841
|
+
The original resource, with the added description
|
|
842
|
+
|
|
843
|
+
Examples:
|
|
844
|
+
```python
|
|
845
|
+
video_segment = video_segment.add_description_optional("description text")
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
```python
|
|
849
|
+
video_segment = video_segment.add_description_optional(None)
|
|
850
|
+
```
|
|
851
|
+
"""
|
|
852
|
+
if is_nonempty_value(description):
|
|
853
|
+
self.add_description(description, permissions, comment, newline_replacement)
|
|
330
854
|
return self
|
|
331
855
|
|
|
332
|
-
def
|
|
333
|
-
self
|
|
856
|
+
def add_keyword(
|
|
857
|
+
self,
|
|
858
|
+
keyword: str,
|
|
859
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
860
|
+
comment: str | None = None,
|
|
861
|
+
) -> VideoSegmentResource:
|
|
862
|
+
"""
|
|
863
|
+
Add a keyword to the resource
|
|
864
|
+
|
|
865
|
+
Args:
|
|
866
|
+
keyword: text
|
|
867
|
+
permissions: optional permissions of this value
|
|
868
|
+
comment: optional comment
|
|
869
|
+
|
|
870
|
+
Returns:
|
|
871
|
+
The original resource, with the added keyword
|
|
872
|
+
|
|
873
|
+
Examples:
|
|
874
|
+
```python
|
|
875
|
+
video_segment = video_segment.add_keyword("keyword")
|
|
876
|
+
```
|
|
877
|
+
"""
|
|
878
|
+
self.values.append(
|
|
879
|
+
SimpleText.new(
|
|
880
|
+
value=keyword,
|
|
881
|
+
prop_name="hasKeyword",
|
|
882
|
+
permissions=permissions,
|
|
883
|
+
comment=comment,
|
|
884
|
+
resource_id=self.res_id,
|
|
885
|
+
)
|
|
886
|
+
)
|
|
334
887
|
return self
|
|
335
888
|
|
|
336
|
-
def
|
|
337
|
-
self,
|
|
889
|
+
def add_keyword_multiple(
|
|
890
|
+
self,
|
|
891
|
+
keywords: Collection[str],
|
|
892
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
893
|
+
comment: str | None = None,
|
|
338
894
|
) -> VideoSegmentResource:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
895
|
+
"""
|
|
896
|
+
Add several keywords to the resource
|
|
897
|
+
|
|
898
|
+
Args:
|
|
899
|
+
keywords: list of texts
|
|
900
|
+
permissions: optional permissions of these values
|
|
901
|
+
comment: optional comment
|
|
902
|
+
|
|
903
|
+
Returns:
|
|
904
|
+
The original resource, with the added keywords
|
|
905
|
+
|
|
906
|
+
Examples:
|
|
907
|
+
```python
|
|
908
|
+
video_segment = video_segment.add_keyword_multiple(["keyword 1", "keyword 2"])
|
|
909
|
+
```
|
|
910
|
+
"""
|
|
911
|
+
vals = check_and_fix_collection_input(keywords, "keywords", self.res_id)
|
|
912
|
+
for v in vals:
|
|
913
|
+
self.add_keyword(v, permissions, comment)
|
|
345
914
|
return self
|
|
346
915
|
|
|
347
|
-
def
|
|
348
|
-
self
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
916
|
+
def add_keyword_optional(
|
|
917
|
+
self,
|
|
918
|
+
keyword: Any,
|
|
919
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
920
|
+
comment: str | None = None,
|
|
921
|
+
) -> VideoSegmentResource:
|
|
922
|
+
"""
|
|
923
|
+
If the value is not empty, add it as keyword, otherwise return the resource unchanged.
|
|
924
|
+
|
|
925
|
+
Args:
|
|
926
|
+
keyword: text or empty value
|
|
927
|
+
permissions: optional permissions of this value
|
|
928
|
+
comment: optional comment
|
|
929
|
+
|
|
930
|
+
Returns:
|
|
931
|
+
The original resource, with the added keyword
|
|
932
|
+
|
|
933
|
+
Examples:
|
|
934
|
+
```python
|
|
935
|
+
video_segment = video_segment.add_keyword_optional("keyword")
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
```python
|
|
939
|
+
video_segment = video_segment.add_keyword_optional(None)
|
|
940
|
+
```
|
|
941
|
+
"""
|
|
942
|
+
if is_nonempty_value(keyword):
|
|
943
|
+
self.add_keyword(keyword, permissions, comment)
|
|
944
|
+
return self
|
|
352
945
|
|
|
353
|
-
def
|
|
354
|
-
self
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
946
|
+
def add_relates_to(
|
|
947
|
+
self,
|
|
948
|
+
relates_to: str,
|
|
949
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
950
|
+
comment: str | None = None,
|
|
951
|
+
) -> VideoSegmentResource:
|
|
952
|
+
"""
|
|
953
|
+
Add a link to a related resource
|
|
954
|
+
|
|
955
|
+
Args:
|
|
956
|
+
relates_to: ID of the related resource
|
|
957
|
+
permissions: optional permissions of this value
|
|
958
|
+
comment: optional comment
|
|
959
|
+
|
|
960
|
+
Returns:
|
|
961
|
+
The original resource, with the added related resource
|
|
962
|
+
|
|
963
|
+
Examples:
|
|
964
|
+
```python
|
|
965
|
+
video_segment = video_segment.add_relates_to("target_resource_id")
|
|
966
|
+
```
|
|
967
|
+
"""
|
|
968
|
+
self.values.append(
|
|
969
|
+
LinkValue.new(
|
|
970
|
+
value=relates_to,
|
|
971
|
+
prop_name="relatesTo",
|
|
972
|
+
permissions=permissions,
|
|
973
|
+
comment=comment,
|
|
974
|
+
resource_id=self.res_id,
|
|
975
|
+
)
|
|
976
|
+
)
|
|
977
|
+
return self
|
|
978
|
+
|
|
979
|
+
def add_relates_to_multiple(
|
|
980
|
+
self,
|
|
981
|
+
relates_to: Collection[str],
|
|
982
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
983
|
+
comment: str | None = None,
|
|
984
|
+
) -> VideoSegmentResource:
|
|
985
|
+
"""
|
|
986
|
+
Add several links to related resources
|
|
987
|
+
|
|
988
|
+
Args:
|
|
989
|
+
relates_to: list of IDs of the related resources
|
|
990
|
+
permissions: optional permissions of these values
|
|
991
|
+
comment: optional comment
|
|
992
|
+
|
|
993
|
+
Returns:
|
|
994
|
+
The original resource, with the added related resources
|
|
995
|
+
|
|
996
|
+
Examples:
|
|
997
|
+
```python
|
|
998
|
+
video_segment = video_segment.add_relates_to_multiple(["target_resource_id_1", "target_resource_id_2"])
|
|
999
|
+
```
|
|
1000
|
+
"""
|
|
1001
|
+
vals = check_and_fix_collection_input(relates_to, "relatesTo", self.res_id)
|
|
1002
|
+
for v in vals:
|
|
1003
|
+
self.add_relates_to(v, permissions, comment)
|
|
1004
|
+
return self
|
|
359
1005
|
|
|
360
|
-
def
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
1006
|
+
def add_relates_to_optional(
|
|
1007
|
+
self,
|
|
1008
|
+
relates_to: Any,
|
|
1009
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1010
|
+
comment: str | None = None,
|
|
1011
|
+
) -> VideoSegmentResource:
|
|
1012
|
+
"""
|
|
1013
|
+
If the value is not empty, add it as related resource, otherwise return the resource unchanged.
|
|
1014
|
+
|
|
1015
|
+
Args:
|
|
1016
|
+
relates_to: ID or the related resource or empty value
|
|
1017
|
+
permissions: optional permissions of this value
|
|
1018
|
+
comment: optional comment
|
|
1019
|
+
|
|
1020
|
+
Returns:
|
|
1021
|
+
The original resource, with the added related resources
|
|
1022
|
+
|
|
1023
|
+
Examples:
|
|
1024
|
+
```python
|
|
1025
|
+
video_segment = video_segment.add_relates_to_optional("target_resource_id")
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
```python
|
|
1029
|
+
video_segment = video_segment.add_relates_to_optional(None)
|
|
1030
|
+
```
|
|
1031
|
+
"""
|
|
1032
|
+
if is_nonempty_value(relates_to):
|
|
1033
|
+
self.add_relates_to(relates_to, permissions, comment)
|
|
1034
|
+
return self
|
|
365
1035
|
|
|
366
1036
|
|
|
367
1037
|
@dataclass
|
|
368
1038
|
class AudioSegmentResource:
|
|
369
1039
|
res_id: str
|
|
370
1040
|
label: str
|
|
371
|
-
segment_of: str
|
|
372
1041
|
segment_bounds: SegmentBounds
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
descriptions: list[str] = field(default_factory=list)
|
|
376
|
-
keywords: list[str] = field(default_factory=list)
|
|
377
|
-
relates_to: list[str] = field(default_factory=list)
|
|
378
|
-
permissions: str = "res-default"
|
|
1042
|
+
values: list[Value]
|
|
1043
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS
|
|
379
1044
|
migration_metadata: MigrationMetadata | None = None
|
|
380
1045
|
|
|
381
|
-
|
|
382
|
-
|
|
1046
|
+
@staticmethod
|
|
1047
|
+
def create_new(
|
|
383
1048
|
res_id: str,
|
|
384
1049
|
label: str,
|
|
385
1050
|
segment_of: str,
|
|
386
|
-
segment_start: float,
|
|
387
|
-
segment_end: float,
|
|
388
|
-
|
|
389
|
-
permissions: str = "res-default",
|
|
1051
|
+
segment_start: float | int | str,
|
|
1052
|
+
segment_end: float | int | str,
|
|
1053
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
390
1054
|
) -> AudioSegmentResource:
|
|
1055
|
+
"""
|
|
1056
|
+
Creates a new audio segment resource, i.e. a time span of an AudioRepresentation.
|
|
1057
|
+
|
|
1058
|
+
[See XML documentation for details](https://docs.dasch.swiss/latest/DSP-TOOLS/file-formats/xml-data-file/#video-segment-and-audio-segment)
|
|
1059
|
+
|
|
1060
|
+
Args:
|
|
1061
|
+
res_id: ID of this audio segment resource
|
|
1062
|
+
label: label of this audio segment resource
|
|
1063
|
+
segment_of: ID of the audio resource that this segment refers to (cardinality 1)
|
|
1064
|
+
segment_start: start of the segment in seconds (cardinality 1)
|
|
1065
|
+
segment_end: end of the segment in seconds (cardinality 1)
|
|
1066
|
+
permissions: permissions of this resource
|
|
1067
|
+
|
|
1068
|
+
Returns:
|
|
1069
|
+
An audio segment resource
|
|
1070
|
+
"""
|
|
1071
|
+
_check_strings(string_to_check=res_id, res_id=res_id, field_name="Resource ID")
|
|
1072
|
+
res_id = str(res_id)
|
|
1073
|
+
lbl = check_and_fix_is_non_empty_string(value=label, res_id=res_id, value_field="label")
|
|
1074
|
+
segment_of_val = LinkValue.new(
|
|
1075
|
+
value=segment_of, prop_name="isSegmentOf", permissions=permissions, comment=None, resource_id=res_id
|
|
1076
|
+
)
|
|
391
1077
|
return AudioSegmentResource(
|
|
392
1078
|
res_id=res_id,
|
|
393
|
-
label=
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
title=title,
|
|
1079
|
+
label=lbl,
|
|
1080
|
+
segment_bounds=SegmentBounds(segment_start, segment_end, permissions, res_id),
|
|
1081
|
+
values=[segment_of_val],
|
|
397
1082
|
permissions=permissions,
|
|
398
1083
|
)
|
|
399
1084
|
|
|
400
|
-
def add_title(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
1085
|
+
def add_title(
|
|
1086
|
+
self,
|
|
1087
|
+
title: str,
|
|
1088
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1089
|
+
comment: str | None = None,
|
|
1090
|
+
) -> AudioSegmentResource:
|
|
1091
|
+
"""
|
|
1092
|
+
Add a title to the resource.
|
|
1093
|
+
|
|
1094
|
+
Args:
|
|
1095
|
+
title: text
|
|
1096
|
+
permissions: permissions of the value
|
|
1097
|
+
comment: comments on the value
|
|
1098
|
+
|
|
1099
|
+
Returns:
|
|
1100
|
+
The original resource, with the added title
|
|
1101
|
+
|
|
1102
|
+
Examples:
|
|
1103
|
+
```python
|
|
1104
|
+
audio_segment = audio_segment.add_title("segment title")
|
|
1105
|
+
```
|
|
1106
|
+
"""
|
|
1107
|
+
existing_title = [x for x in self.values if x.prop_name == "hasTitle"]
|
|
1108
|
+
if any(existing_title):
|
|
1109
|
+
_warn_value_exists(
|
|
1110
|
+
old_value=existing_title.pop(0).value, new_value=title, value_field="title", res_id=self.res_id
|
|
1111
|
+
)
|
|
1112
|
+
self.values.append(
|
|
1113
|
+
SimpleText.new(
|
|
1114
|
+
value=title,
|
|
1115
|
+
prop_name="hasTitle",
|
|
1116
|
+
permissions=permissions,
|
|
1117
|
+
comment=comment,
|
|
1118
|
+
resource_id=self.res_id,
|
|
1119
|
+
)
|
|
1120
|
+
)
|
|
1121
|
+
return self
|
|
1122
|
+
|
|
1123
|
+
def add_title_optional(
|
|
1124
|
+
self,
|
|
1125
|
+
title: Any,
|
|
1126
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1127
|
+
comment: str | None = None,
|
|
1128
|
+
) -> AudioSegmentResource:
|
|
1129
|
+
"""
|
|
1130
|
+
If the value is not empty, add it as title, otherwise return the resource unchanged.
|
|
1131
|
+
|
|
1132
|
+
Args:
|
|
1133
|
+
title: text or empty value
|
|
1134
|
+
permissions: permissions of the value
|
|
1135
|
+
comment: comments on the value
|
|
1136
|
+
|
|
1137
|
+
Returns:
|
|
1138
|
+
The original resource, with the added title
|
|
1139
|
+
|
|
1140
|
+
Examples:
|
|
1141
|
+
```python
|
|
1142
|
+
audio_segment = audio_segment.add_title("segment title")
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
```python
|
|
1146
|
+
audio_segment = audio_segment.add_title(None)
|
|
1147
|
+
```
|
|
1148
|
+
"""
|
|
1149
|
+
if is_nonempty_value(title):
|
|
1150
|
+
self.add_title(title, permissions, comment)
|
|
1151
|
+
return self
|
|
1152
|
+
|
|
1153
|
+
def add_comment(
|
|
1154
|
+
self,
|
|
1155
|
+
text: str,
|
|
1156
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1157
|
+
comment: str | None = None,
|
|
1158
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
1159
|
+
) -> AudioSegmentResource:
|
|
1160
|
+
"""
|
|
1161
|
+
Add a comment to the resource
|
|
1162
|
+
|
|
1163
|
+
Args:
|
|
1164
|
+
text: text of the comment
|
|
1165
|
+
permissions: optional permissions of this value
|
|
1166
|
+
comment: optional comment about this comment
|
|
1167
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
1168
|
+
|
|
1169
|
+
Returns:
|
|
1170
|
+
The original resource, with the added comment
|
|
1171
|
+
|
|
1172
|
+
Examples:
|
|
1173
|
+
```python
|
|
1174
|
+
audio_segment = audio_segment.add_comment("comment text")
|
|
1175
|
+
```
|
|
1176
|
+
"""
|
|
1177
|
+
self.values.append(
|
|
1178
|
+
Richtext.new(
|
|
1179
|
+
value=text,
|
|
1180
|
+
prop_name="hasComment",
|
|
1181
|
+
permissions=permissions,
|
|
1182
|
+
comment=comment,
|
|
1183
|
+
resource_id=self.res_id,
|
|
1184
|
+
newline_replacement=newline_replacement,
|
|
1185
|
+
)
|
|
1186
|
+
)
|
|
404
1187
|
return self
|
|
405
1188
|
|
|
406
|
-
def
|
|
407
|
-
self
|
|
1189
|
+
def add_comment_multiple(
|
|
1190
|
+
self,
|
|
1191
|
+
texts: Collection[str],
|
|
1192
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1193
|
+
comment: str | None = None,
|
|
1194
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
1195
|
+
) -> AudioSegmentResource:
|
|
1196
|
+
"""
|
|
1197
|
+
Add several comments to the resource
|
|
1198
|
+
|
|
1199
|
+
Args:
|
|
1200
|
+
texts: list of texts
|
|
1201
|
+
permissions: optional permissions of these values
|
|
1202
|
+
comment: optional comment about these comments
|
|
1203
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
1204
|
+
|
|
1205
|
+
Returns:
|
|
1206
|
+
The original resource, with the added comments
|
|
1207
|
+
|
|
1208
|
+
Examples:
|
|
1209
|
+
```python
|
|
1210
|
+
audio_segment = audio_segment.add_comment_multiple(["comment 1", "comment 2"])
|
|
1211
|
+
```
|
|
1212
|
+
"""
|
|
1213
|
+
vals = check_and_fix_collection_input(texts, "hasComment", self.res_id)
|
|
1214
|
+
for v in vals:
|
|
1215
|
+
self.add_comment(v, permissions, comment, newline_replacement)
|
|
408
1216
|
return self
|
|
409
1217
|
|
|
410
|
-
def
|
|
411
|
-
self
|
|
1218
|
+
def add_comment_optional(
|
|
1219
|
+
self,
|
|
1220
|
+
text: Any,
|
|
1221
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1222
|
+
comment: str | None = None,
|
|
1223
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
1224
|
+
) -> AudioSegmentResource:
|
|
1225
|
+
"""
|
|
1226
|
+
If the value is not empty, add it as comment, otherwise return the resource unchanged.
|
|
1227
|
+
|
|
1228
|
+
Args:
|
|
1229
|
+
text: text of the comment (or empty value)
|
|
1230
|
+
permissions: optional permissions of this value
|
|
1231
|
+
comment: optional comment about this comment
|
|
1232
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
1233
|
+
|
|
1234
|
+
Returns:
|
|
1235
|
+
The original resource, with the added comment
|
|
1236
|
+
|
|
1237
|
+
Examples:
|
|
1238
|
+
```python
|
|
1239
|
+
audio_segment = audio_segment.add_comment_optional("comment text")
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
```python
|
|
1243
|
+
audio_segment = audio_segment.add_comment_optional(None)
|
|
1244
|
+
```
|
|
1245
|
+
"""
|
|
1246
|
+
if is_nonempty_value(text):
|
|
1247
|
+
self.add_comment(text, permissions, comment, newline_replacement)
|
|
412
1248
|
return self
|
|
413
1249
|
|
|
414
|
-
def add_description(
|
|
415
|
-
self
|
|
1250
|
+
def add_description(
|
|
1251
|
+
self,
|
|
1252
|
+
description: str,
|
|
1253
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1254
|
+
comment: str | None = None,
|
|
1255
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
1256
|
+
) -> AudioSegmentResource:
|
|
1257
|
+
"""
|
|
1258
|
+
Add a description to the resource
|
|
1259
|
+
|
|
1260
|
+
Args:
|
|
1261
|
+
description: text
|
|
1262
|
+
permissions: optional permissions of this value
|
|
1263
|
+
comment: optional comment
|
|
1264
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
1265
|
+
|
|
1266
|
+
Returns:
|
|
1267
|
+
The original resource, with the added description
|
|
1268
|
+
|
|
1269
|
+
Examples:
|
|
1270
|
+
```python
|
|
1271
|
+
audio_segment = audio_segment.add_description("description text")
|
|
1272
|
+
```
|
|
1273
|
+
"""
|
|
1274
|
+
self.values.append(
|
|
1275
|
+
Richtext.new(
|
|
1276
|
+
value=description,
|
|
1277
|
+
prop_name="hasDescription",
|
|
1278
|
+
permissions=permissions,
|
|
1279
|
+
comment=comment,
|
|
1280
|
+
resource_id=self.res_id,
|
|
1281
|
+
newline_replacement=newline_replacement,
|
|
1282
|
+
)
|
|
1283
|
+
)
|
|
416
1284
|
return self
|
|
417
1285
|
|
|
418
|
-
def
|
|
419
|
-
self
|
|
1286
|
+
def add_description_multiple(
|
|
1287
|
+
self,
|
|
1288
|
+
descriptions: Collection[str],
|
|
1289
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1290
|
+
comment: str | None = None,
|
|
1291
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
1292
|
+
) -> AudioSegmentResource:
|
|
1293
|
+
"""
|
|
1294
|
+
Add several descriptions to the resource
|
|
1295
|
+
|
|
1296
|
+
Args:
|
|
1297
|
+
descriptions: list of texts
|
|
1298
|
+
permissions: optional permissions of these values
|
|
1299
|
+
comment: optional comment
|
|
1300
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
1301
|
+
|
|
1302
|
+
Returns:
|
|
1303
|
+
The original resource, with the added descriptions
|
|
1304
|
+
|
|
1305
|
+
Examples:
|
|
1306
|
+
```python
|
|
1307
|
+
audio_segment = audio_segment.add_description_multiple(["description 1", "description 2"])
|
|
1308
|
+
```
|
|
1309
|
+
"""
|
|
1310
|
+
vals = check_and_fix_collection_input(descriptions, "description", self.res_id)
|
|
1311
|
+
for v in vals:
|
|
1312
|
+
self.add_description(v, permissions, comment, newline_replacement)
|
|
420
1313
|
return self
|
|
421
1314
|
|
|
422
|
-
def
|
|
423
|
-
self
|
|
1315
|
+
def add_description_optional(
|
|
1316
|
+
self,
|
|
1317
|
+
description: Any,
|
|
1318
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1319
|
+
comment: str | None = None,
|
|
1320
|
+
newline_replacement: NewlineReplacement = NewlineReplacement.LINEBREAK,
|
|
1321
|
+
) -> AudioSegmentResource:
|
|
1322
|
+
"""
|
|
1323
|
+
If the value is not empty, add it as description, otherwise return the resource unchanged.
|
|
1324
|
+
|
|
1325
|
+
Args:
|
|
1326
|
+
description: text or empty value
|
|
1327
|
+
permissions: optional permissions of this value
|
|
1328
|
+
comment: optional comment
|
|
1329
|
+
newline_replacement: options how to deal with `\\n` inside the text value. Default: replace with `<br/>`
|
|
1330
|
+
|
|
1331
|
+
Returns:
|
|
1332
|
+
The original resource, with the added description
|
|
1333
|
+
|
|
1334
|
+
Examples:
|
|
1335
|
+
```python
|
|
1336
|
+
audio_segment = audio_segment.add_description_optional("description text")
|
|
1337
|
+
```
|
|
1338
|
+
|
|
1339
|
+
```python
|
|
1340
|
+
audio_segment = audio_segment.add_description_optional(None)
|
|
1341
|
+
```
|
|
1342
|
+
"""
|
|
1343
|
+
if is_nonempty_value(description):
|
|
1344
|
+
self.add_description(description, permissions, comment, newline_replacement)
|
|
424
1345
|
return self
|
|
425
1346
|
|
|
426
|
-
def
|
|
427
|
-
self
|
|
1347
|
+
def add_keyword(
|
|
1348
|
+
self,
|
|
1349
|
+
keyword: str,
|
|
1350
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1351
|
+
comment: str | None = None,
|
|
1352
|
+
) -> AudioSegmentResource:
|
|
1353
|
+
"""
|
|
1354
|
+
Add a keyword to the resource
|
|
1355
|
+
|
|
1356
|
+
Args:
|
|
1357
|
+
keyword: text
|
|
1358
|
+
permissions: optional permissions of this value
|
|
1359
|
+
comment: optional comment
|
|
1360
|
+
|
|
1361
|
+
Returns:
|
|
1362
|
+
The original resource, with the added keyword
|
|
1363
|
+
|
|
1364
|
+
Examples:
|
|
1365
|
+
```python
|
|
1366
|
+
audio_segment = audio_segment.add_keyword("keyword")
|
|
1367
|
+
```
|
|
1368
|
+
"""
|
|
1369
|
+
self.values.append(
|
|
1370
|
+
SimpleText.new(
|
|
1371
|
+
value=keyword,
|
|
1372
|
+
prop_name="hasKeyword",
|
|
1373
|
+
permissions=permissions,
|
|
1374
|
+
comment=comment,
|
|
1375
|
+
resource_id=self.res_id,
|
|
1376
|
+
)
|
|
1377
|
+
)
|
|
428
1378
|
return self
|
|
429
1379
|
|
|
430
|
-
def
|
|
431
|
-
self
|
|
1380
|
+
def add_keyword_multiple(
|
|
1381
|
+
self,
|
|
1382
|
+
keywords: Collection[str],
|
|
1383
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1384
|
+
comment: str | None = None,
|
|
1385
|
+
) -> AudioSegmentResource:
|
|
1386
|
+
"""
|
|
1387
|
+
Add several keywords to the resource
|
|
1388
|
+
|
|
1389
|
+
Args:
|
|
1390
|
+
keywords: list of texts
|
|
1391
|
+
permissions: optional permissions of these values
|
|
1392
|
+
comment: optional comment
|
|
1393
|
+
|
|
1394
|
+
Returns:
|
|
1395
|
+
The original resource, with the added keywords
|
|
1396
|
+
|
|
1397
|
+
Examples:
|
|
1398
|
+
```python
|
|
1399
|
+
audio_segment = audio_segment.add_keyword_multiple(["keyword 1", "keyword 2"])
|
|
1400
|
+
```
|
|
1401
|
+
"""
|
|
1402
|
+
vals = check_and_fix_collection_input(keywords, "keywords", self.res_id)
|
|
1403
|
+
for v in vals:
|
|
1404
|
+
self.add_keyword(v, permissions, comment)
|
|
432
1405
|
return self
|
|
433
1406
|
|
|
434
|
-
def
|
|
435
|
-
self
|
|
1407
|
+
def add_keyword_optional(
|
|
1408
|
+
self,
|
|
1409
|
+
keyword: Any,
|
|
1410
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1411
|
+
comment: str | None = None,
|
|
1412
|
+
) -> AudioSegmentResource:
|
|
1413
|
+
"""
|
|
1414
|
+
If the value is not empty, add it as keyword, otherwise return the resource unchanged.
|
|
1415
|
+
|
|
1416
|
+
Args:
|
|
1417
|
+
keyword: text or empty value
|
|
1418
|
+
permissions: optional permissions of this value
|
|
1419
|
+
comment: optional comment
|
|
1420
|
+
|
|
1421
|
+
Returns:
|
|
1422
|
+
The original resource, with the added keyword
|
|
1423
|
+
|
|
1424
|
+
Examples:
|
|
1425
|
+
```python
|
|
1426
|
+
audio_segment = audio_segment.add_keyword_optional("keyword")
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
```python
|
|
1430
|
+
audio_segment = audio_segment.add_keyword_optional(None)
|
|
1431
|
+
```
|
|
1432
|
+
"""
|
|
1433
|
+
if is_nonempty_value(keyword):
|
|
1434
|
+
self.add_keyword(keyword, permissions, comment)
|
|
436
1435
|
return self
|
|
437
1436
|
|
|
438
|
-
def
|
|
439
|
-
self,
|
|
1437
|
+
def add_relates_to(
|
|
1438
|
+
self,
|
|
1439
|
+
relates_to: str,
|
|
1440
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1441
|
+
comment: str | None = None,
|
|
440
1442
|
) -> AudioSegmentResource:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
1443
|
+
"""
|
|
1444
|
+
Add a link to a related resource
|
|
1445
|
+
|
|
1446
|
+
Args:
|
|
1447
|
+
relates_to: ID of the related resource
|
|
1448
|
+
permissions: optional permissions of this value
|
|
1449
|
+
comment: optional comment
|
|
1450
|
+
|
|
1451
|
+
Returns:
|
|
1452
|
+
The original resource, with the added related resource
|
|
1453
|
+
|
|
1454
|
+
Examples:
|
|
1455
|
+
```python
|
|
1456
|
+
audio_segment = audio_segment.add_relates_to("target_resource_id")
|
|
1457
|
+
```
|
|
1458
|
+
"""
|
|
1459
|
+
self.values.append(
|
|
1460
|
+
LinkValue.new(
|
|
1461
|
+
value=relates_to,
|
|
1462
|
+
prop_name="relatesTo",
|
|
1463
|
+
permissions=permissions,
|
|
1464
|
+
comment=comment,
|
|
1465
|
+
resource_id=self.res_id,
|
|
445
1466
|
)
|
|
446
|
-
self.migration_metadata = MigrationMetadata(creation_date=creation_date, iri=iri, ark=ark, res_id=self.res_id)
|
|
447
|
-
return self
|
|
448
|
-
|
|
449
|
-
def serialise(self) -> etree._Element:
|
|
450
|
-
self._check_for_and_convert_unexpected_input()
|
|
451
|
-
res_ele = self._serialise_resource_element()
|
|
452
|
-
res_ele.extend(_serialise_segment_children(self))
|
|
453
|
-
return res_ele
|
|
454
|
-
|
|
455
|
-
def _check_for_and_convert_unexpected_input(self) -> None:
|
|
456
|
-
self.comments = _transform_unexpected_input(self.comments, "comments", self.res_id)
|
|
457
|
-
self.descriptions = _transform_unexpected_input(self.descriptions, "descriptions", self.res_id)
|
|
458
|
-
self.keywords = _transform_unexpected_input(self.keywords, "keywords", self.res_id)
|
|
459
|
-
self.relates_to = _transform_unexpected_input(self.relates_to, "relates_to", self.res_id)
|
|
460
|
-
_validate_segment(self)
|
|
461
|
-
|
|
462
|
-
def _serialise_resource_element(self) -> etree._Element:
|
|
463
|
-
attribs = {"label": self.label, "id": self.res_id}
|
|
464
|
-
if self.permissions:
|
|
465
|
-
attribs["permissions"] = self.permissions
|
|
466
|
-
return etree.Element(f"{DASCH_SCHEMA}audio-segment", attrib=attribs, nsmap=XML_NAMESPACE_MAP)
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
def _check_strings(string_to_check: str, res_id: str, field_name: str) -> None:
|
|
470
|
-
if not is_string_like(string_to_check):
|
|
471
|
-
msg = (
|
|
472
|
-
f"The resource with the ID '{res_id}' has an invalid string at the following location:\n"
|
|
473
|
-
f"Field: {field_name} | Value: {string_to_check}"
|
|
474
1467
|
)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
def
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
case str():
|
|
542
|
-
return [value]
|
|
543
|
-
case _:
|
|
544
|
-
_warn_unexpected_value(value, prop_name, res_id)
|
|
545
|
-
return [str(value)]
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
def _warn_unexpected_value(value: Any, prop_name: str, res_id: str | None) -> None:
|
|
549
|
-
msg = (
|
|
550
|
-
f"The resource: {res_id} should have a list of strings for the field '{prop_name}'. "
|
|
551
|
-
f"Your input: '{value}' is of type {type(value)}."
|
|
552
|
-
)
|
|
553
|
-
warnings.warn(DspToolsUserWarning(msg))
|
|
1468
|
+
return self
|
|
1469
|
+
|
|
1470
|
+
def add_relates_to_multiple(
|
|
1471
|
+
self,
|
|
1472
|
+
relates_to: Collection[str],
|
|
1473
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1474
|
+
comment: str | None = None,
|
|
1475
|
+
) -> AudioSegmentResource:
|
|
1476
|
+
"""
|
|
1477
|
+
Add several links to related resources
|
|
1478
|
+
|
|
1479
|
+
Args:
|
|
1480
|
+
relates_to: list of IDs of the related resources
|
|
1481
|
+
permissions: optional permissions of these values
|
|
1482
|
+
comment: optional comment
|
|
1483
|
+
|
|
1484
|
+
Returns:
|
|
1485
|
+
The original resource, with the added related resources
|
|
1486
|
+
|
|
1487
|
+
Examples:
|
|
1488
|
+
```python
|
|
1489
|
+
audio_segment = audio_segment.add_relates_to_multiple(["target_resource_id_1", "target_resource_id_2"])
|
|
1490
|
+
```
|
|
1491
|
+
"""
|
|
1492
|
+
vals = check_and_fix_collection_input(relates_to, "relatesTo", self.res_id)
|
|
1493
|
+
for v in vals:
|
|
1494
|
+
self.add_relates_to(v, permissions, comment)
|
|
1495
|
+
return self
|
|
1496
|
+
|
|
1497
|
+
def add_relates_to_optional(
|
|
1498
|
+
self,
|
|
1499
|
+
relates_to: Any,
|
|
1500
|
+
permissions: Permissions = Permissions.PROJECT_SPECIFIC_PERMISSIONS,
|
|
1501
|
+
comment: str | None = None,
|
|
1502
|
+
) -> AudioSegmentResource:
|
|
1503
|
+
"""
|
|
1504
|
+
If the value is not empty, add it as related resource, otherwise return the resource unchanged.
|
|
1505
|
+
|
|
1506
|
+
Args:
|
|
1507
|
+
relates_to: ID of the related resource or empty value
|
|
1508
|
+
permissions: optional permissions of this value
|
|
1509
|
+
comment: optional comment
|
|
1510
|
+
|
|
1511
|
+
Returns:
|
|
1512
|
+
The original resource, with the added related resources
|
|
1513
|
+
|
|
1514
|
+
Examples:
|
|
1515
|
+
```python
|
|
1516
|
+
audio_segment = audio_segment.add_relates_to_optional("target_resource_id")
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
```python
|
|
1520
|
+
audio_segment = audio_segment.add_relates_to_optional(None)
|
|
1521
|
+
```
|
|
1522
|
+
"""
|
|
1523
|
+
if is_nonempty_value(relates_to):
|
|
1524
|
+
self.add_relates_to(relates_to, permissions, comment)
|
|
1525
|
+
return self
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
def _check_strings(
|
|
1529
|
+
*, string_to_check: str, res_id: str, prop_name: str | None = None, field_name: str | None = None
|
|
1530
|
+
) -> None:
|
|
1531
|
+
if not is_nonempty_value(string_to_check):
|
|
1532
|
+
msg_info = MessageInfo("The entered string is not valid.", res_id, prop_name, field_name)
|
|
1533
|
+
emit_xmllib_input_warning(msg_info)
|
|
554
1534
|
|
|
555
1535
|
|
|
556
|
-
def _warn_value_exists(old_value: Any, new_value: Any,
|
|
557
|
-
"""Emits a warning if a values is not in the expected format."""
|
|
1536
|
+
def _warn_value_exists(*, old_value: Any, new_value: Any, res_id: str | None, value_field: str | None = None) -> None:
|
|
558
1537
|
msg = (
|
|
559
|
-
f"
|
|
1538
|
+
f"This resource already has a value in this location. "
|
|
560
1539
|
f"The old value '{old_value}' is being replace with '{new_value}'."
|
|
561
1540
|
)
|
|
562
|
-
|
|
1541
|
+
msg_info = MessageInfo(msg, res_id, field=value_field)
|
|
1542
|
+
emit_xmllib_input_warning(msg_info)
|