Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes, simplifications and improvements to logging messages #28

Merged
merged 9 commits into from
Dec 12, 2023
43 changes: 18 additions & 25 deletions capella2polarion/elements/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
}
PHYSICAL_COMPONENT_TYPES = {
"PhysicalComponentNode": "PhysicalComponent",
"PhysicalActorNode": "PhysicalComponent",
"PhysicalComponentBehavior": "PhysicalComponent",
"PhysicalActorBehavior": "PhysicalComponent",
}
POL2CAPELLA_TYPES: dict[str, str] = (
{
Expand Down Expand Up @@ -163,13 +165,7 @@ def patch_work_items(ctx: dict[str, t.Any]) -> None:
new_work_item, back_links[old_work_item.id]
)

api_helper.patch_work_item(
ctx["API"],
new_work_item,
old_work_item,
old_work_item.title,
"element",
)
api_helper.patch_work_item(ctx["API"], new_work_item, old_work_item)


def get_types(ctx: dict[str, t.Any]) -> set[str]:
Expand Down Expand Up @@ -234,25 +230,22 @@ def _fix_components(
elements[typ] = actors
elements[xtype] = components

nodes: list[common.GenericElement] = []
behaviors: list[common.GenericElement] = []
components = []
nature_mapping: dict[str, tuple[list[common.GenericElement], str]] = {
"UNSET": ([], "PhysicalComponent"),
"NODE": ([], "PhysicalComponentNode"),
"BEHAVIOR": ([], "PhysicalComponentBehavior"),
"NODE_actor": ([], "PhysicalActorNode"),
"BEHAVIOR_actor": ([], "PhysicalActorBehavior"),
}
for obj in elements.get("PhysicalComponent", []):
if obj.nature is not None and obj.nature.name == "NODE":
nodes.append(obj)
type_map[obj.uuid] = "PhysicalComponentNode"
elif obj.nature is not None and obj.nature.name == "BEHAVIOR":
behaviors.append(obj)
type_map[obj.uuid] = "PhysicalComponentBehavior"
else:
components.append(obj)

if nodes:
elements["PhysicalComponentNode"] = nodes
if behaviors:
elements["PhysicalComponentBehavior"] = behaviors
if components:
elements["PhysicalComponent"] = components
postfix = "_actor" if obj.is_actor else ""
container, xtype = nature_mapping[f"{str(obj.nature)}{postfix}"]
container.append(obj)
type_map[obj.uuid] = xtype

for container, xtype in nature_mapping.values():
if container:
elements[xtype] = container


def make_model_elements_index(ctx: dict[str, t.Any]) -> None:
Expand Down
20 changes: 8 additions & 12 deletions capella2polarion/elements/api_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ def patch_work_item(
api: polarion_api.OpenAPIPolarionProjectClient,
new: serialize.CapellaWorkItem,
old: serialize.CapellaWorkItem,
name: str,
_type: str,
):
"""Patch a given WorkItem.

Expand All @@ -29,16 +27,11 @@ def patch_work_item(
The updated CapellaWorkItem
old
The CapellaWorkItem currently present on polarion
name
The name of the object, which should be displayed in log
messages.
_type
The type of element, which should be shown in log messages.
"""
if new == old:
return

log_args = (old.id, _type, name)
log_args = (old.id, new.type, new.title)
logger.info("Update work item %r for model %s %r...", *log_args)
if "uuid_capella" in new.additional_attributes:
del new.additional_attributes["uuid_capella"]
Expand All @@ -52,18 +45,21 @@ def patch_work_item(
handle_links(
old.linked_work_items,
new.linked_work_items,
("Delete", _type, name),
("Delete", *log_args[1:]),
api.delete_work_item_links,
)
handle_links(
new.linked_work_items,
old.linked_work_items,
("Create", _type, name),
("Create", *log_args[1:]),
api.create_work_item_links,
)
except polarion_api.PolarionApiException as error:
wi = f"{old.id}({_type} {name})"
logger.error("Updating work item %r failed. %s", wi, error.args[0])
logger.error(
"Updating work item %r (%s %s) failed. %s",
*log_args,
error.args[0],
)
ewuerger marked this conversation as resolved.
Show resolved Hide resolved


def handle_links(
Expand Down
7 changes: 6 additions & 1 deletion capella2polarion/elements/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import collections.abc as cabc
import functools
import logging
import types
import typing as t
from collections import defaultdict
from itertools import chain
Expand Down Expand Up @@ -50,6 +51,8 @@ def create_work_items(
missing_types: set[str] = set()
for work_item in _work_items:
assert work_item is not None
assert work_item.title is not None
assert work_item.type is not None
if work_item.type in valid_types:
work_items.append(work_item)
else:
Expand All @@ -60,7 +63,9 @@ def create_work_items(
"%r are missing in the capella2polarion configuration",
", ".join(missing_types),
)
ctx["WORK_ITEMS"] = {wi.uuid_capella: wi for wi in work_items}
ctx["WORK_ITEMS"] = types.MappingProxyType(
{wi.uuid_capella: wi for wi in work_items}
)
return work_items


Expand Down
49 changes: 30 additions & 19 deletions capella2polarion/elements/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@

from capella2polarion.elements import helpers

RE_DESCR_LINK_PATTERN = re.compile(r"<a href=\"hlink://([^\"]+)\">[^<]+<\/a>")
RE_DESCR_LINK_PATTERN = re.compile(
r"<a href=\"hlink://([^\"]+)\">([^<]+)<\/a>"
)
RE_DESCR_DELETED_PATTERN = re.compile(
f"<deleted element ({chelpers.RE_VALID_UUID.pattern})>"
)
Expand Down Expand Up @@ -143,8 +145,9 @@ def _get_requirement_types_text(
continue

if not (req.type and req.text):
identifier = req.long_name or req.name or req.summary or req.uuid
logger.warning(
"Requirement without text or type found %r", req.name
"Requirement without text or type found %r", identifier
)
continue

Expand Down Expand Up @@ -174,7 +177,7 @@ def _sanitize_description(
) -> tuple[list[str], markupsafe.Markup]:
referenced_uuids: list[str] = []
replaced_markup = RE_DESCR_LINK_PATTERN.sub(
lambda match: replace_markup(match, ctx, referenced_uuids), descr
lambda match: replace_markup(match, ctx, referenced_uuids, 2), descr
)

def repair_images(node: etree._Element) -> None:
Expand Down Expand Up @@ -208,18 +211,24 @@ def replace_markup(
match: re.Match,
ctx: dict[str, t.Any],
referenced_uuids: list[str],
non_matcher: cabc.Callable[[str], str] = lambda i: i,
default_group: int = 1,
) -> str:
"""Replace UUID references in a ``match`` with a work item link.

If the UUID doesn't correspond to an existing work item the original
text is returned.
"""
uuid = match.group(1)
try:
ctx["MODEL"].by_uuid(uuid)
ewuerger marked this conversation as resolved.
Show resolved Hide resolved
except KeyError:
logger.error("Found link to non-existing model element: %r", uuid)
return strike_through(match.group(default_group))
if pid := ctx["POLARION_ID_MAP"].get(uuid):
referenced_uuids.append(uuid)
return POLARION_WORK_ITEM_URL.format(pid=pid)
return non_matcher(match.group(0))
logger.warning("Found reference to non-existing work item: %r", uuid)
return match.group(default_group)


def include_pre_and_post_condition(
Expand All @@ -232,11 +241,6 @@ def get_condition(cap: PrePostConditionElement, name: str) -> str:
return ""
return condition.specification["capella:linkedText"].striptags()

def strike_through(string: str) -> str:
if match := RE_DESCR_DELETED_PATTERN.match(string):
string = match.group(1)
return f'<span style="text-decoration: line-through;">{string}</span>'

def matcher(match: re.Match) -> str:
return strike_through(replace_markup(match, ctx, []))

Expand All @@ -254,6 +258,13 @@ def matcher(match: re.Match) -> str:
return work_item


def strike_through(string: str) -> str:
"""Return a striked-through html span from given ``string``."""
if match := RE_DESCR_DELETED_PATTERN.match(string):
string = match.group(1)
return f'<span style="text-decoration: line-through;">{string}</span>'


def get_linked_text(
obj: capellacore.Constraint, ctx: dict[str, t.Any]
) -> markupsafe.Markup:
Expand All @@ -280,7 +291,7 @@ def _condition(html: bool, value: str) -> CapellaWorkItem.Condition:
return {"type": _type, "value": value}


def component_or_actor(
def _include_actor_in_type(
obj: cs.Component, ctx: dict[str, t.Any]
) -> CapellaWorkItem:
"""Return attributes for a ``Component``."""
Expand All @@ -294,15 +305,15 @@ def component_or_actor(
return work_item


def physical_component(
def _include_nature_in_type(
obj: pa.PhysicalComponent, ctx: dict[str, t.Any]
) -> CapellaWorkItem:
"""Return attributes for a ``PhysicalComponent``."""
work_item = component_or_actor(obj, ctx)
work_item = _include_actor_in_type(obj, ctx)
xtype = work_item.type
if obj.nature is not None:
# pylint: disable-next=attribute-defined-outside-init
work_item.type = f"{xtype}{obj.nature.name.capitalize()}"
nature = [obj.nature.name, ""][obj.nature == "UNSET"]
# pylint: disable-next=attribute-defined-outside-init
work_item.type = f"{xtype}{nature.capitalize()}"
return work_item


Expand All @@ -311,11 +322,11 @@ def physical_component(
]
SERIALIZERS: dict[str, Serializer] = {
"CapabilityRealization": include_pre_and_post_condition,
"LogicalComponent": component_or_actor,
"LogicalComponent": _include_actor_in_type,
"OperationalCapability": include_pre_and_post_condition,
"PhysicalComponent": physical_component,
"PhysicalComponent": _include_nature_in_type,
"SystemCapability": include_pre_and_post_condition,
"SystemComponent": component_or_actor,
"SystemComponent": _include_actor_in_type,
"Scenario": include_pre_and_post_condition,
"Constraint": constraint,
}
3 changes: 1 addition & 2 deletions tests/test_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
TEST_WE_UUID = "e37510b9-3166-4f80-a919-dfaac9b696c7"
TEST_E_UUID = "4bf0356c-89dd-45e9-b8a6-e0332c026d33"
TEST_WE_DESCR = (
'<p><span class="polarion-rte-link" data-type="workItem" '
'id="fake" data-item-id="TEST" data-option-id="long"/></p>\n'
'<p><span style="text-decoration: line-through;">Weather</span></p>\n'
)
TEST_ACTOR_UUID = "08e02248-504d-4ed8-a295-c7682a614f66"
TEST_PHYS_COMP = "b9f9a83c-fb02-44f7-9123-9d86326de5f1"
Expand Down