From a383ef8888aff26242fdd084999f7779aa63cfe7 Mon Sep 17 00:00:00 2001 From: Daniel Klein Date: Tue, 7 Jul 2026 10:03:08 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8Feature:=20Implement=20get=5Fdescripti?= =?UTF-8?q?on=5Ffrom=5Fsubmodel=20and=20get=5Fdisplay=5Fname=5Ffrom=5Fsubm?= =?UTF-8?q?odel=20functions;=20add=20tests=20for=20new=20functionality.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Klein --- aas_standard_parser/submodel_parser.py | 48 ++++++++++++++++++++++++++ tests/test_aimc_parser.py | 12 ++++--- tests/test_data/aimc_submodel.json | 6 ++++ tests/test_submodel_parser.py | 34 ++++++++++++++++++ 4 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 tests/test_submodel_parser.py diff --git a/aas_standard_parser/submodel_parser.py b/aas_standard_parser/submodel_parser.py index b9c11a6..b203e54 100644 --- a/aas_standard_parser/submodel_parser.py +++ b/aas_standard_parser/submodel_parser.py @@ -7,6 +7,54 @@ logger = logging.getLogger(__name__) +def get_description_from_submodel(submodel: model.Submodel, language: str = "en") -> str | None: + """Get the description from a submodel. + + :param submodel: The submodel to extract the description from. + :param language: The language code for the description. + :return: The description string if found, otherwise None. + """ + if submodel.description is None: + logger.warning(f"No description found for submodel {submodel.id_short}") + return None + + keys = submodel.description.keys() + + if keys is None or len(keys) == 0: + logger.warning(f"No description keys found for submodel {submodel.id_short}") + return None + + if language not in keys: + logger.warning(f"Description for language '{language}' not found in submodel {submodel.id_short}") + return None + + return submodel.description.get(language) + + +def get_display_name_from_submodel(submodel: model.Submodel, language: str = "en") -> str | None: + """Get the display name from a submodel. + + :param submodel: The submodel to extract the display name from. + :param language: The language code for the display name. + :return: The display name string if found, otherwise None. + """ + if submodel.display_name is None: + logger.warning(f"No display name found for submodel {submodel.id_short}") + return None + + keys = submodel.display_name.keys() + + if keys is None or len(keys) == 0: + logger.warning(f"No display name keys found for submodel {submodel.id_short}") + return None + + if language not in keys: + logger.warning(f"Display name for language '{language}' not found in submodel {submodel.id_short}") + return None + + return submodel.display_name.get(language) + + def get_submodel_element_by_id_short_path(submodel: model.Submodel, id_short_path: str) -> model.SubmodelElement: """Retrieve a specific submodel element from the submodel at a specific idShort path. diff --git a/tests/test_aimc_parser.py b/tests/test_aimc_parser.py index feaff50..a608946 100644 --- a/tests/test_aimc_parser.py +++ b/tests/test_aimc_parser.py @@ -7,19 +7,19 @@ @pytest.fixture(scope="module") -def aimc_submodel() -> model.Property: +def aimc_submodel() -> model.Submodel: # create a Submodel return create_submodel_from_file("tests/test_data/aimc_submodel.json") -def test_001_get_mapping_configuration_root_element(aimc_submodel: model.submodel): +def test_001_get_mapping_configuration_root_element(aimc_submodel: model.Submodel): root_element = aimc_parser.get_mapping_configuration_root_element(aimc_submodel) assert root_element is not None assert root_element.id_short == "MappingConfigurations" -def test_002_get_mapping_configuration_elements(aimc_submodel: model.submodel): +def test_002_get_mapping_configuration_elements(aimc_submodel: model.Submodel): configuration_elements = aimc_parser.get_mapping_configuration_elements( aimc_submodel ) @@ -30,7 +30,8 @@ def test_002_get_mapping_configuration_elements(aimc_submodel: model.submodel): configuration_element = configuration_elements[0] assert isinstance(configuration_element, model.SubmodelElementCollection) -def test_003_parse_mapping_configuration_element(aimc_submodel: model.submodel): + +def test_003_parse_mapping_configuration_element(aimc_submodel: model.Submodel): configuration_elements = aimc_parser.get_mapping_configuration_elements( aimc_submodel ) @@ -42,7 +43,8 @@ def test_003_parse_mapping_configuration_element(aimc_submodel: model.submodel): _check_interface_ref(configuration) _check_relations(configuration) -def test_004_parse_mapping_configurations(aimc_submodel: model.submodel): + +def test_004_parse_mapping_configurations(aimc_submodel: model.Submodel): mapping_configurations = aimc_parser.parse_mapping_configurations(aimc_submodel) assert mapping_configurations is not None diff --git a/tests/test_data/aimc_submodel.json b/tests/test_data/aimc_submodel.json index 8157d8c..3575344 100644 --- a/tests/test_data/aimc_submodel.json +++ b/tests/test_data/aimc_submodel.json @@ -21,6 +21,12 @@ "text": "This Submodel specifies an information model for describing the mapping of interface(s) of an asset service or asset-related service already described in an Asset Interfaces Description (AID) Submodel." } ], + "displayName": [ + { + "language": "en", + "text": "AIMC Submodel." + } + ], "idShort": "AssetInterfacesMappingConfiguration", "submodelElements": [ { diff --git a/tests/test_submodel_parser.py b/tests/test_submodel_parser.py new file mode 100644 index 0000000..a2e1925 --- /dev/null +++ b/tests/test_submodel_parser.py @@ -0,0 +1,34 @@ +import pytest +from basyx.aas import model + +from aas_standard_parser.classes.aimc_parser_classes import MappingConfiguration, SourceSinkRelation, ReferenceProperties +import aas_standard_parser.submodel_parser as submodel_parser +from aas_standard_parser.utils import create_submodel_from_file + + +@pytest.fixture(scope="module") +def aimc_submodel() -> model.Submodel: + # create a Submodel + return create_submodel_from_file("tests/test_data/aimc_submodel.json") + +def test_001a_get_description_from_submodel(aimc_submodel: model.Submodel): + description = submodel_parser.get_description_from_submodel(aimc_submodel) + + assert description is not None + assert description == 'This Submodel specifies an information model for describing the mapping of interface(s) of an asset service or asset-related service already described in an Asset Interfaces Description (AID) Submodel.' + +def test_001b_get_description_from_submodel(aimc_submodel: model.Submodel): + description = submodel_parser.get_description_from_submodel(aimc_submodel, "de") + + assert description is None + +def test_002a_get_display_name_from_submodel(aimc_submodel: model.Submodel): + display_name = submodel_parser.get_display_name_from_submodel(aimc_submodel) + + assert display_name is not None + assert display_name == 'AIMC Submodel.' + +def test_002b_get_display_name_from_submodel(aimc_submodel: model.Submodel): + display_name = submodel_parser.get_display_name_from_submodel(aimc_submodel, "de") + + assert display_name is None