Skip to content

Commit

Permalink
merge: Merge pull request #28 from DSD-DBS/fix-descr-reference-links
Browse files Browse the repository at this point in the history
Various fixes, simplifications and improvements to logging messages
  • Loading branch information
ewuerger authored Dec 12, 2023
2 parents 9b1a9b8 + e638978 commit 6968931
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 59 deletions.
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],
)


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)
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

0 comments on commit 6968931

Please sign in to comment.