Skip to content

Commit

Permalink
fix: Generation of grouped linkfields (#114)
Browse files Browse the repository at this point in the history
Before the linkfield wasn't taken into account
when creating the grouped linkfields.
Additionally the feature of generating grouped
linkfields and the reverse is now configurable
by using the `--grouped-links-custom-fields`
flag or setting the `CAPELLA2POLARION_GROUPED_LINKS_CUSTOM_FIELDS`
environment variable.
  • Loading branch information
ewuerger authored Sep 30, 2024
1 parent 0a43f94 commit 1eeead7
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 60 deletions.
8 changes: 8 additions & 0 deletions capella2polarion/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,20 @@ def print_cli_state(capella2polarion_cli: Capella2PolarionCli) -> None:
envvar="CAPELLA2POLARION_ROLE_PREFIX",
default="",
)
@click.option(
"--grouped-links-custom-fields / --no-grouped-links-custom-fields",
envvar="CAPELLA2POLARION_GROUPED_LINKS_CUSTOM_FIELDS",
is_flag=True,
default=True,
)
@click.pass_context
def synchronize(
ctx: click.core.Context,
synchronize_config: typing.TextIO,
force_update: bool,
type_prefix: str,
role_prefix: str,
grouped_links_custom_fields: bool,
) -> None:
"""Synchronise model elements."""
capella_to_polarion_cli: Capella2PolarionCli = ctx.obj
Expand Down Expand Up @@ -153,6 +160,7 @@ def synchronize(
polarion_worker.polarion_data_repo,
generate_links=True,
generate_attachments=True,
generate_grouped_links_custom_fields=grouped_links_custom_fields,
)

polarion_worker.compare_and_update_work_items(converter.converter_session)
Expand Down
49 changes: 26 additions & 23 deletions capella2polarion/converters/link_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ def __init__(
converter_config.DIAGRAM_ELEMENTS_SERIALIZER: self._handle_diagram_reference_links, # pylint: disable=line-too-long
}

self._link_field_groups: dict[str, list[polarion_api.WorkItemLink]] = (
defaultdict(list)
)

def create_links_for_work_item(
self, uuid: str
) -> list[polarion_api.WorkItemLink]:
Expand All @@ -58,6 +62,7 @@ def create_links_for_work_item(
work_item = converter_data.work_item
assert work_item is not None
assert work_item.id is not None
self._link_field_groups.clear()
new_links: list[polarion_api.WorkItemLink] = []
link_errors: list[str] = []
for link_config in converter_data.type_config.links:
Expand All @@ -66,9 +71,7 @@ def create_links_for_work_item(
try:
assert work_item.id is not None
if serializer:
new_links.extend(
serializer(obj, work_item.id, role_id, {})
)
links = serializer(obj, work_item.id, role_id, {})
else:
refs = _resolve_attribute(obj, link_config.capella_attr)
new: cabc.Iterable[str]
Expand All @@ -88,9 +91,10 @@ def create_links_for_work_item(
new = set(
self._get_work_item_ids(work_item.id, new, role_id)
)
new_links.extend(
self._create(work_item.id, role_id, new, {})
)
links = self._create(work_item.id, role_id, new, {})

new_links.extend(links)
self._link_field_groups[link_config.link_field].extend(links)
except Exception as error:
error_message = make_link_logging_message(
f"{type(error).__name__} {str(error)}",
Expand Down Expand Up @@ -223,23 +227,22 @@ def create_grouped_link_fields(
assert work_item is not None
wi = f"[{work_item.id}]({work_item.type} {work_item.title})"
logger.debug("Building grouped links for work item %r...", wi)
for role, grouped_links in _group_by(
"role", work_item.linked_work_items
).items():
if (config := find_link_config(data, role)) is not None:
if back_links is not None and config.reverse_field:
for link in grouped_links:
back_links.setdefault(
link.secondary_work_item_id, {}
).setdefault(config.reverse_field, []).append(link)

if config.link_field:
self._create_link_fields(
work_item,
config.link_field,
grouped_links,
config=config,
)
for link_config in data.type_config.links:
grouped_links = self._link_field_groups[link_config.link_field]

if back_links is not None and link_config.reverse_field:
for link in grouped_links:
back_links.setdefault(
link.secondary_work_item_id, {}
).setdefault(link_config.reverse_field, []).append(link)

if grouped_links:
self._create_link_fields(
work_item,
link_config.link_field,
grouped_links,
config=link_config,
)

def _create_link_fields(
self,
Expand Down
16 changes: 12 additions & 4 deletions capella2polarion/converters/model_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def generate_work_items(
polarion_data_repo: polarion_repo.PolarionDataRepository,
generate_links: bool = False,
generate_attachments: bool = False,
generate_grouped_links_custom_fields: bool = False,
) -> dict[str, data_models.CapellaWorkItem]:
"""Return a work items mapping from model elements for Polarion.
Expand All @@ -100,6 +101,9 @@ def generate_work_items(
generate_attachments
A boolean flag to control attachments generation. For SVG
attachments, PNGs are generated and attached automatically.
generate_grouped_links_custom_fields
A boolean flag to control grouped links custom fields
generation.
"""
serializer = element_converter.CapellaWorkItemSerializer(
self.model,
Expand All @@ -113,13 +117,16 @@ def generate_work_items(
assert work_item.type is not None

if generate_links:
self.generate_work_item_links(polarion_data_repo)
self.generate_work_item_links(
polarion_data_repo, generate_grouped_links_custom_fields
)

return {wi.uuid_capella: wi for wi in work_items}

def generate_work_item_links(
self,
polarion_data_repo: polarion_repo.PolarionDataRepository,
generate_grouped_links_custom_fields: bool,
):
"""Generate links for all work items and add custom fields for them."""
back_links: dict[str, dict[str, list[polarion_api.WorkItemLink]]] = {}
Expand All @@ -140,9 +147,10 @@ def generate_work_item_links(
links = link_serializer.create_links_for_work_item(uuid)
converter_data.work_item.linked_work_items = links

link_serializer.create_grouped_link_fields(
converter_data, back_links
)
if generate_grouped_links_custom_fields:
link_serializer.create_grouped_link_fields(
converter_data, back_links
)

for uuid, converter_data in self.converter_session.items():
if converter_data.work_item is None:
Expand Down
27 changes: 11 additions & 16 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
DOCUMENT_TEMPLATES = TEST_DOCUMENT_ROOT / "templates"
DOCUMENT_TEXT_WORK_ITEMS = "document_work_items.html.j2"
DOCUMENT_WORK_ITEMS_CROSS_PROJECT = "work_items_cross_project.html.j2"
LINK_CONFIG = converter_config.LinkConfig(
capella_attr="attribute",
polarion_role="attribute",
link_field="attribute",
reverse_field="attribute_reverse",
)


@pytest.fixture
Expand Down Expand Up @@ -103,16 +109,10 @@ class UnsupportedFakeModelObject(FakeModelObject):
"""A ``FakeModelObject`` which shouldn't be migrated."""


class BaseObjectContainer:
def __init__(
self,
c2p_cli: cli.Capella2PolarionCli,
pw: polarion_worker.CapellaPolarionWorker,
mc: model_converter.ModelConverter,
) -> None:
self.c2pcli: cli.Capella2PolarionCli = c2p_cli
self.pw: polarion_worker.CapellaPolarionWorker = pw
self.mc = mc
class BaseObjectContainer(t.NamedTuple):
c2pcli: cli.Capella2PolarionCli
pw: polarion_worker.CapellaPolarionWorker
mc: model_converter.ModelConverter


# pylint: disable=redefined-outer-name
Expand Down Expand Up @@ -141,12 +141,7 @@ def base_object(

fake = FakeModelObject("uuid1", name="Fake 1")
fake_model_type_config = converter_config.CapellaTypeConfig(
"fakeModelObject",
links=[
converter_config.LinkConfig(
capella_attr="attribute", polarion_role="attribute"
)
],
"fakeModelObject", links=[LINK_CONFIG]
)

mc = model_converter.ModelConverter(
Expand Down
2 changes: 2 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def test_migrate_model_elements(cli_mocks: CLIMocks):
"synchronize",
"--synchronize-config",
str(TEST_MODEL_ELEMENTS_CONFIG),
"--grouped-links-custom-fields",
]

result = testing.CliRunner().invoke(main.cli, command, terminal_width=60)
Expand All @@ -140,6 +141,7 @@ def test_migrate_model_elements(cli_mocks: CLIMocks):
assert cli_mocks.generate_work_items.call_args_list[1][1] == {
"generate_links": True,
"generate_attachments": True,
"generate_grouped_links_custom_fields": True,
}
assert cli_mocks.delete_work_items.call_count == 1
assert cli_mocks.patch_work_items.call_count == 1
Expand Down
Loading

0 comments on commit 1eeead7

Please sign in to comment.