diff --git a/README.md b/README.md index d7f3c76..7995d0d 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,21 @@ [![License](https://img.shields.io/github/license/ewoks-kit/ewoksdraw)](https://github.com/ewoks-kit/ewoksdraw/blob/main/LICENSE.md) [![Coverage](https://codecov.io/gh/ewoks-kit/ewoksdraw/branch/main/graph/badge.svg)](https://codecov.io/gh/ewoks-kit/ewoksdraw) -A project to generate SVG mock-ups out of Ewoks workflows. +A library to generate SVG mock-ups out of Ewoks workflows. ## Quick start ```bash pip install "git+https://github.com/ewoks-kit/ewoksdraw.git" -ewoksdraw ``` + +```python +from ewoks import load_graph +from ewoksdraw import graph_to_svg + +# Load a graph with `ewoks.load_graph` +graph = load_graph(...) + +# Generate the SVG +graph_to_svg(graph, output_path='my_workflow.svg') +``` \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index d53353b..90aaf87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,9 +28,6 @@ Repository = "https://github.com/ewoks-kit/ewoksdraw/" Issues = "https://github.com/ewoks-kit/ewoksdraw/issues/" Changelog = "https://github.com/ewoks-kit/ewoksdraw/-/blob/main/CHANGELOG.md" -[project.scripts] -ewoksdraw = "ewoksdraw.main:main" - [project.optional-dependencies] test = [ "pytest >=7", diff --git a/src/ewoksdraw/__init__.py b/src/ewoksdraw/__init__.py index 08a1c15..13fd854 100644 --- a/src/ewoksdraw/__init__.py +++ b/src/ewoksdraw/__init__.py @@ -1 +1,36 @@ -# This file is intentionally left blank. +import random +from pathlib import Path + +from ewokscore.graph import TaskGraph +from ewokscore.graph.inputs import _get_all_node_inputs +from ewokscore.graph.inputs import _get_all_task_output_names + +from .svg import SvgBackground +from .svg import SvgCanvas +from .svg import SvgTask + + +def graph_to_svg(graph: TaskGraph, output_path: str | Path): + canvas_width = 500 + canvas_height = 500 + + canvas = SvgCanvas(width=canvas_width, height=canvas_height) + svg_background = SvgBackground(canvas_width, canvas_height) + canvas.add_element(svg_background) + + for node_id, node_attrs in graph.graph.nodes.items(): + node_inputs = _get_all_node_inputs(node_id, node_attrs) + node_outputs = _get_all_task_output_names( + node_attrs["task_type"], node_attrs["task_identifier"] + ) + svg_task = SvgTask( + task_name=node_id, + input_names=[n.name for n in node_inputs], + output_names=node_outputs, + ) + + svg_task.translate(x=random.randint(5, 400), y=random.randint(5, 400)) + + canvas.add_element(svg_task) + + canvas.draw(output_path) diff --git a/src/ewoksdraw/main.py b/src/ewoksdraw/main.py deleted file mode 100644 index 6993511..0000000 --- a/src/ewoksdraw/main.py +++ /dev/null @@ -1,60 +0,0 @@ -import argparse -import random - -from ewokscore import load_graph -from ewokscore.graph.inputs import _get_all_node_inputs -from ewokscore.graph.inputs import _get_all_task_output_names - -from .svg import SvgBackground -from .svg import SvgCanvas -from .svg import SvgTask - - -def main(): - parser = argparse.ArgumentParser(description="Saves a worfklow as SVG") - - parser.add_argument("workflow", type=str, help="Workflow to save to SVG") - parser.add_argument( - "--test", - help="The 'workflow' argument refers to the name of a test graph.", - action="store_true", - ) - parser.add_argument( - "--output_svg", type=str, required=True, help="Output SVG file path" - ) - - args = parser.parse_args() - - canvas_width = 500 - canvas_height = 500 - - canvas = SvgCanvas(width=canvas_width, height=canvas_height) - svg_background = SvgBackground(canvas_width, canvas_height) - canvas.add_element(svg_background) - - if args.test: - representation = "test_core" - else: - representation = None - graph = load_graph(args.workflow, representation=representation).graph - - for node_id, node_attrs in graph.nodes.items(): - node_inputs = _get_all_node_inputs(node_id, node_attrs) - node_outputs = _get_all_task_output_names( - node_attrs["task_type"], node_attrs["task_identifier"] - ) - svg_task = SvgTask( - task_name=node_id, - input_names=[n.name for n in node_inputs], - output_names=node_outputs, - ) - - svg_task.translate(x=random.randint(5, 400), y=random.randint(5, 400)) - - canvas.add_element(svg_task) - - canvas.draw(args.output_svg) - - -if __name__ == "__main__": - main() diff --git a/src/ewoksdraw/tests/test_graph_to_svg.py b/src/ewoksdraw/tests/test_graph_to_svg.py new file mode 100644 index 0000000..5010968 --- /dev/null +++ b/src/ewoksdraw/tests/test_graph_to_svg.py @@ -0,0 +1,31 @@ +from pathlib import Path +from xml.etree import ElementTree + +import pytest +from ewokscore import load_graph +from ewokscore.tests.examples.graphs import get_graph +from ewokscore.tests.examples.graphs import graph_names + +from ewoksdraw import graph_to_svg + + +def _get_svg_groups(output_path: Path): + tree = ElementTree.parse(output_path) + root = tree.getroot() + return [child for child in root if child.tag.endswith("g")] + + +@pytest.mark.parametrize("graph_name", graph_names()) +def test_groups_are_matching_nodes(graph_name, tmp_path: Path): + output_path = tmp_path / "test.svg" + + graph, _ = get_graph(graph_name) + ewoksgraph = load_graph(graph) + + graph_to_svg(ewoksgraph, output_path) + + assert output_path.is_file() + + groups = _get_svg_groups(output_path) + for group, node_name in zip(groups, ewoksgraph.graph.nodes.keys()): + assert group[0].text == node_name diff --git a/src/ewoksdraw/tests/test_main.py b/src/ewoksdraw/tests/test_main.py deleted file mode 100644 index 928b740..0000000 --- a/src/ewoksdraw/tests/test_main.py +++ /dev/null @@ -1,25 +0,0 @@ -import subprocess -from pathlib import Path -from xml.etree import ElementTree - -from ewokscore import load_graph - - -def _get_svg_groups(output_path: Path): - tree = ElementTree.parse(output_path) - root = tree.getroot() - return [child for child in root if child.tag.endswith("g")] - - -def test_groups_are_matching_nodes(tmp_path: Path): - output_path = tmp_path / "test.svg" - subprocess.run( - ("ewoksdraw", "demo", "--test", "--output_svg", output_path), check=True - ) - - assert output_path.is_file() - - groups = _get_svg_groups(output_path) - demo_graph = load_graph("demo", representation="test_core").graph - for group, node_name in zip(groups, demo_graph.nodes.keys()): - assert group[0].text == node_name