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

fix: Improve the position of the conditions on the edges #116

Merged
merged 6 commits into from
Jul 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 52 additions & 9 deletions ansibleplaybookgrapher/postprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
import os
from typing import Dict

from ansible.utils.display import Display
from lxml import etree
from svg.path import parse_path

from ansibleplaybookgrapher.graph import PlaybookNode

display = Display()
DISPLAY_PREFIX = "postprocessor:"

JQUERY = "https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"
SVG_NAMESPACE = "http://www.w3.org/2000/svg"

Expand Down Expand Up @@ -149,6 +154,32 @@ def _insert_graph_representation(self, graph_representation: PlaybookNode):

element.append(root_subelement)

def _get_text_path_start_offset(self, path_element, text: str) -> str:
"""
Get the start offset where the edge label should begin
:param path_element:
:param text:
:return:
"""
# Get Bézier curve
path_segments = parse_path(path_element.get("d"))
# FIXME: apply the translation to the segments ?
# TRANSLATE_PATTERN = re.compile(".*translate\((?P<x>[+-]?[0-9]*[.]?[0-9]+) (?P<y>[+-]?[0-9]*[.]?[0-9]+)\).*")
# transform_attribute = self.root.xpath("//*[@id='graph0']", namespaces={"ns": SVG_NAMESPACE})[0].get("transform")

# The segments usually contain 3 elements: One MoveTo and one or two CubicBezier objects.
# This is relatively slow to compute. Decreasing the "error" will drastically slow down the post-processing
segment_length = path_segments.length(error=1e-4)
text_length = len(text)
# We put the label closer to the target node
offset_factor = 0.76

start_offset = segment_length * offset_factor - text_length
msg = f"{DISPLAY_PREFIX} {len(path_segments)} segment(s) found for the path '{path_element.get('id')}', "
msg += f"segment_length={segment_length}, start_offset={start_offset}, text_length={text_length}"
display.vvvvv(msg)
return str(start_offset)

def _curve_text_on_edges(self):
"""
Update the text on each edge to curve it based on the edge
Expand All @@ -160,22 +191,34 @@ def _curve_text_on_edges(self):
)

for edge in edge_elements:
path_element = edge.find(".//path", namespaces=self.root.nsmap)
path_elements = edge.findall(".//path", namespaces=self.root.nsmap)
display.vvvvv(
f"{DISPLAY_PREFIX} {len(path_elements)} path(s) found on the edge '{edge.get('id')}'"
)
text_element = edge.find(".//text", namespaces=self.root.nsmap)

# Define an ID for the path so that we can reference it explicitly
path_id = f"path_{edge.get('id')}"
# Even though we may have more than one path, we only care about a single on.
# We have more than one path (edge) pointing to a single task if role containing the task is used more than
# once.
path_element = path_elements[0]
path_element.set("id", path_id)

# Create a curved textPath
# Create a curved textPath: the text will follow the path
text_path = etree.Element("textPath")
text_path.set("{http://www.w3.org/1999/xlink}href", f"#{path_id}")
text_path.set("text-anchor", "middle")
text_path.set("startOffset", "50%")
text_path.text = text_element.text

offset = self._get_text_path_start_offset(path_element, text_path.text)
text_path.set("startOffset", offset)

text_element.append(text_path)

# Move a little the text
text_element.set("dy", "-0.5%")
# Remove unnecessary attributes
text_element.attrib.pop("x")
text_element.attrib.pop("y")
# The more paths we have, the more we move the text from the path
dy = -0.2 - (len(path_elements) - 1) * 0.4
text_element.set("dy", f"{dy}%")
# Remove unnecessary attributes and clear the text
text_element.attrib.pop("x", "")
text_element.attrib.pop("y", "")
text_element.text = None
2 changes: 1 addition & 1 deletion ansibleplaybookgrapher/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def convert_when_to_str(when: List) -> str:

# Convert each element in the list to str
when_to_str = list(map(str, when))
return f"[when: {' and '.join(when_to_str)}]"
return f"[when: {' and '.join(when_to_str)}]".strip().replace("\n", "")


def hash_value(value: str) -> str:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ ansible-core>=2.11,<2.13
graphviz>=0.18,<1
colour<1
lxml<5
svg.path<7