diff --git a/capella2polarion/converters/converter_config.py b/capella2polarion/converters/converter_config.py index dc89c121..be8f372d 100644 --- a/capella2polarion/converters/converter_config.py +++ b/capella2polarion/converters/converter_config.py @@ -16,6 +16,12 @@ class CapellaTypeConfig: p_type: str | None = None converter: str | None = None links: list[str] = dataclasses.field(default_factory=list) + actor: bool | None = None + nature: bool | None = None + + +def _default_type_conversion(c_type: str) -> str: + return c_type[0].lower() + c_type[1:] class ConverterConfig: @@ -23,7 +29,7 @@ class ConverterConfig: def __init__(self, synchronize_config: typing.TextIO): config_dict = yaml.safe_load(synchronize_config) - self._layer_configs: dict[str, dict[str, CapellaTypeConfig]] = {} + self._layer_configs: dict[str, dict[str, list[CapellaTypeConfig]]] = {} self._global_configs: dict[str, CapellaTypeConfig] = {} # We handle the cross layer config separately as global_configs global_config_dict = config_dict.pop("*", {}) @@ -31,64 +37,121 @@ def __init__(self, synchronize_config: typing.TextIO): global_links = all_type_config.get("links", []) self.__global_config = CapellaTypeConfig(links=global_links) + def _read_capella_type_configs(conf: dict | list | None) -> list[dict]: + if conf is None: + return [{}] + if isinstance(conf, dict): + return [conf] + + # We want to have the most generic config first followed by those + # having actor set to None + return sorted( + conf, + key=lambda c: int(c.get("actor") is not None) + + 2 * int(c.get("nature") is not None), + ) + for c_type, type_config in global_config_dict.items(): type_config = type_config or {} self._global_configs[c_type] = CapellaTypeConfig( - type_config.get("polarion_type"), + type_config.get("polarion_type") + or _default_type_conversion(c_type), type_config.get("serializer"), type_config.get("links", []) + global_links, + type_config.get("actor"), + type_config.get("nature"), ) for layer, type_configs in config_dict.items(): self._layer_configs[layer] = {} - for c_type, type_config in type_configs.items(): - self._layer_configs[layer][c_type] = CapellaTypeConfig( - type_config.get("polarion_type") - or self._global_configs.get( - c_type, self.__global_config - ).p_type, - type_config.get("serializer") - or self._global_configs.get( - c_type, self.__global_config - ).converter, - type_config.get("links", []) - + self._global_configs.get( - c_type, self.__global_config - ).links, - ) - - def _default_type_conversion(self, c_type: str) -> str: - return c_type[0].lower() + c_type[1:] - - def _get_type_configs( - self, layer: str, c_type: str + for c_type, c_type_config in type_configs.items(): + type_configs = _read_capella_type_configs(c_type_config) + self._layer_configs[layer][c_type] = [] + for type_config in type_configs: + closest_config = ( + self.get_type_config( + layer, + c_type, + type_config.get("actor"), + type_config.get("nature"), + ) + or self.__global_config + ) + self._layer_configs[layer][c_type].append( + CapellaTypeConfig( + type_config.get("polarion_type") + or closest_config.p_type + or _default_type_conversion(c_type), + type_config.get("serializer") + or closest_config.converter, + type_config.get("links", []) + + closest_config.links, + type_config.get("actor"), + type_config.get("nature"), + ) + ) + + def get_type_config( + self, + layer: str, + c_type: str, + actor: bool | None = None, + nature: str | None = None, ) -> CapellaTypeConfig | None: - return self._layer_configs.get(layer, {}).get( - c_type - ) or self._global_configs.get(c_type) - - def get_polarion_type(self, layer: str, c_type: str) -> str: - """Return polarion type for a given layer and Capella type.""" - type_config = ( - self._get_type_configs(layer, c_type) or self.__global_config - ) - return type_config.p_type or self._default_type_conversion(c_type) - - def get_serializer(self, layer: str, c_type: str) -> str | None: - """Return the serializer name for a given layer and Capella type.""" - type_config = ( - self._get_type_configs(layer, c_type) or self.__global_config - ) - return type_config.converter - - def get_links(self, layer: str, c_type: str) -> list[str]: - """Return the list of link types for a given layer and Capella type.""" - type_config = ( - self._get_type_configs(layer, c_type) or self.__global_config - ) - return type_config.links - - def __contains__(self, item: tuple[str, str]): + """Get the type config for a given layer and capella_type.""" + layer_configs = self._layer_configs.get(layer, {}).get(c_type) + global_config = self._global_configs.get(c_type) + if layer_configs: + if config := next( + filter( + lambda c: c is not None + and c.actor == actor + and c.nature == nature, + layer_configs, + ), + None, + ): + return config + + if config := next( + filter( + lambda c: c is not None + and c.actor == actor + and c.nature is None, + layer_configs, + ), + None, + ): + return config + + if config := next( + filter( + lambda c: c is not None + and c.actor is None + and c.nature == nature, + layer_configs, + ), + None, + ): + return config + + if config := next( + filter( + lambda c: c is not None + and c.actor is None + and c.nature is None, + layer_configs, + ), + None, + ): + return config + + return global_config + + def __contains__( + self, + item: tuple[str, str, typing.Optional[bool], typing.Optional[str]], + ): """Check if there is a config for a given layer and Capella type.""" - layer, c_type = item - return self._get_type_configs(layer, c_type) is not None + layer, c_type, actor, nature = item + return self.get_type_config(layer, c_type, actor, nature) is not None diff --git a/capella2polarion/converters/converter_data_session.py b/capella2polarion/converters/converter_data_session.py new file mode 100644 index 00000000..81f73ac2 --- /dev/null +++ b/capella2polarion/converters/converter_data_session.py @@ -0,0 +1,25 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 +"""A module to store data during the conversion process.""" +from __future__ import annotations + +import dataclasses + +from capellambse.model import GenericElement + +from capella2polarion import data_models as dm +from capella2polarion.converters import converter_config + + +@dataclasses.dataclass +class ConverterData: + """Data class holding all information needed during Conversion.""" + + layer: str + type_config: converter_config.CapellaTypeConfig + capella_element: GenericElement | None = None + work_item: dm.CapellaWorkItem | None = None + description_references: list[str] = dataclasses.field(default_factory=list) + + +ConverterSession: dict[str, ConverterData] diff --git a/tests/data/model_elements/new_config.yaml b/tests/data/model_elements/new_config.yaml index bb3dfb70..4064bc24 100644 --- a/tests/data/model_elements/new_config.yaml +++ b/tests/data/model_elements/new_config.yaml @@ -21,3 +21,20 @@ oa: # Specify below - exchange_items OperationalCapability: serializer: include_pre_and_post_condition +pa: + PhysicalComponent: + - actor: false + nature: null + polarion_type: PhysicalComponent + - actor: false + nature: NODE + polarion_type: PhysicalComponentNode + - actor: false + nature: BEHAVIOR + polarion_type: PhysicalComponentBehavior + - actor: true + nature: NODE + polarion_type: PhysicalActorNode + - actor: true + nature: BEHAVIOR + polarion_type: PhysicalActorBehavior