Skip to content

Commit

Permalink
feat(serializer): Add add_attributes serializer
Browse files Browse the repository at this point in the history
For now this serializer is only supports adding
enum attributes as custom fields.
  • Loading branch information
ewuerger committed Dec 12, 2024
1 parent ee26a4f commit fac3949
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 44 deletions.
89 changes: 45 additions & 44 deletions capella2polarion/converters/converter_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,50 @@ class CapellaTypeConfig:

def __post_init__(self):
"""Post processing for the initialization."""
self.converters = _force_dict(self.converters)
self.converters = self._force_dict()

def _force_dict(self) -> dict[str, dict[str, t.Any]]:
match self.converters:
case None:
return {}
case str():
return {self.converters: {}}
case list():
return {c: {} for c in self.converters}
case dict():
return self._filter_converter_config()
case _:
raise TypeError("Unsupported Type")

def _filter_converter_config(self) -> dict[str, dict[str, t.Any]]:
custom_converters = (
"include_pre_and_post_condition",
"linked_text_as_description",
"add_attributes",
"add_context_diagram",
"add_tree_diagram",
"add_jinja_fields",
"jinja_as_description",
)
filtered_config = {}
assert isinstance(self.converters, dict)
for name, params in self.converters.items():
params = params or {}
if name not in custom_converters:
logger.error("Unknown converter in config %r", name)
continue

if name in ("add_context_diagram", "add_tree_diagram"):
assert isinstance(params, dict)
params = _filter_context_diagram_config(params)

if name in ("add_attributes"):
assert isinstance(params, list) # type: ignore[unreachable]
params = {"attributes": params} # type: ignore[unreachable]

filtered_config[name] = params

return filtered_config


def _default_type_conversion(c_type: str) -> str:
Expand Down Expand Up @@ -283,7 +326,7 @@ def config_matches(config: CapellaTypeConfig | None, **kwargs: t.Any) -> bool:


def _read_capella_type_configs(
conf: dict[str, t.Any] | list[dict[str, t.Any]] | None
conf: dict[str, t.Any] | list[dict[str, t.Any]] | None,
) -> list[dict]:
if conf is None:
return [{}]
Expand All @@ -299,55 +342,13 @@ def _read_capella_type_configs(
)


def _force_dict(
config: str | list[str] | dict[str, dict[str, t.Any]] | None
) -> dict[str, dict[str, t.Any]]:
match config:
case None:
return {}
case str():
return {config: {}}
case list():
return {c: {} for c in config}
case dict():
return _filter_converter_config(config)
case _:
raise TypeError("Unsupported Type")


def add_prefix(polarion_type: str, prefix: str) -> str:
"""Add a prefix to the given ``polarion_type``."""
if prefix:
return f"{prefix}_{polarion_type}"
return polarion_type


def _filter_converter_config(
config: dict[str, dict[str, t.Any]]
) -> dict[str, dict[str, t.Any]]:
custom_converters = (
"include_pre_and_post_condition",
"linked_text_as_description",
"add_context_diagram",
"add_tree_diagram",
"add_jinja_fields",
"jinja_as_description",
)
filtered_config = {}
for name, params in config.items():
params = params or {}
if name not in custom_converters:
logger.error("Unknown converter in config %r", name)
continue

if name in ("add_context_diagram", "add_tree_diagram"):
params = _filter_context_diagram_config(params)

filtered_config[name] = params

return filtered_config


def _filter_context_diagram_config(
config: dict[str, t.Any]
) -> dict[str, t.Any]:
Expand Down
37 changes: 37 additions & 0 deletions capella2polarion/converters/element_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import collections
import enum
import hashlib
import logging
import mimetypes
Expand Down Expand Up @@ -66,6 +67,16 @@ def _format(texts: list[str]) -> dict[str, str]:
return requirement_types


def _resolve_capella_attribute(
element: m.ModelElement | m.Diagram, attribute: str
) -> polarion_api.TextContent:
value = getattr(element, attribute)
if isinstance(value, enum.Enum):
return polarion_api.TextContent(type="string", value=value.name)

raise ValueError("Unsupported attribute type: %r", value)


class CapellaWorkItemSerializer(polarion_html_helper.JinjaRendererMixin):
"""The general serializer class for CapellaWorkItems."""

Expand Down Expand Up @@ -455,6 +466,32 @@ def __generic_work_item(

return converter_data.work_item

def _add_attributes(
self,
converter_data: data_session.ConverterData,
attributes: list[dict[str, t.Any]],
):
assert converter_data.work_item is not None
for attribute in attributes:
try:
value = _resolve_capella_attribute(
converter_data.capella_element, attribute["capella_attr"]
)
setattr(
converter_data.work_item, attribute["polarion_id"], value
)
except AttributeError:
logger.error(
"Attribute %r not found on %r",
attribute["capella_attr"],
converter_data.type_config.p_type,
)
continue
except ValueError as error:
logger.error(error.args[0])

return converter_data.work_item

def _diagram(
self,
converter_data: data_session.ConverterData,
Expand Down
4 changes: 4 additions & 0 deletions docs/source/features/sync.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ specific serializer alone:
| linked_text_as_description | A serializer resolving ``Constraint`` s and their |
| | linked text. |
+--------------------------------------+------------------------------------------------------+
| add_attributes | A serializer adding arbitrary attributes as custom |
| | fields to the work item. For now only supports enum |
| | attributes! |
+--------------------------------------+------------------------------------------------------+
| add_context_diagram | A serializer adding a context diagram to the work |
| | item. This requires node.js to be installed. |
| | The Capella objects where ``context_diagram`` is |
Expand Down
29 changes: 29 additions & 0 deletions tests/test_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,35 @@ def test_generic_work_item(
assert work_item == data_model.CapellaWorkItem(**expected)
assert status == "open"

def test_add_attributes(self, model: capellambse.MelodyModel):
converters = {
"add_attributes": [
{"capella_attr": "nature", "polarion_id": "nature"},
{"capella_attr": "kind", "polarion_id": "kind"},
]
}
type_config = converter_config.CapellaTypeConfig(
"PhysicalComponent", converters, []
)
serializer = element_converter.CapellaWorkItemSerializer(
model,
polarion_repo.PolarionDataRepository(),
{
TEST_PHYS_COMP: data_session.ConverterData(
"pa",
type_config,
model.by_uuid(TEST_PHYS_COMP),
)
},
True,
)

work_item = serializer.serialize(TEST_PHYS_COMP)

assert work_item is not None
assert work_item.nature == {"type": "string", "value": "UNSET"}
assert work_item.kind == {"type": "string", "value": "UNSET"}

@staticmethod
def test_add_context_diagram(model: capellambse.MelodyModel):
type_config = converter_config.CapellaTypeConfig(
Expand Down

0 comments on commit fac3949

Please sign in to comment.