From ec884625089a82cd908f652e5e2e846ad9eab46a Mon Sep 17 00:00:00 2001 From: Vlada Sokol <67763989+vladasokol@users.noreply.github.com> Date: Sat, 2 Nov 2024 22:10:29 -0400 Subject: [PATCH] Initial version of successor DAG with ladder node placement. --- tests/cl/runtime/plots/test_successor_dag.png | Bin 0 -> 40536 bytes .../runtime/plots/test_successor_dag_plot.py | 136 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 tests/cl/runtime/plots/test_successor_dag.png create mode 100644 tests/cl/runtime/plots/test_successor_dag_plot.py diff --git a/tests/cl/runtime/plots/test_successor_dag.png b/tests/cl/runtime/plots/test_successor_dag.png new file mode 100644 index 0000000000000000000000000000000000000000..b95b78f762beed55b19ac170a4c99d54263bc532 GIT binary patch literal 40536 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 literal 0 HcmV?d00001 diff --git a/tests/cl/runtime/plots/test_successor_dag_plot.py b/tests/cl/runtime/plots/test_successor_dag_plot.py new file mode 100644 index 00000000..d0b4e83a --- /dev/null +++ b/tests/cl/runtime/plots/test_successor_dag_plot.py @@ -0,0 +1,136 @@ +# Copyright (C) 2023-present The Project Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from cl.runtime.context.testing_context import TestingContext +from dataclasses import dataclass +from typing import List, Optional +import networkx as nx +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle +from cl.runtime.testing.pytest.pytest_fixtures import local_dir_fixture + +def test_smoke(local_dir_fixture): + with TestingContext() as context: + + @dataclass + class Node: + title: str + successors: Optional[List['Node']] = None + + # Define the nodes with successors + staff_a = Node(title="Staff A") + staff_b = Node(title="Staff B") + staff_c = Node(title="Staff C") + staff_d = Node(title="Staff D") + team_1 = Node(title="Team A Lead", successors=[staff_a, staff_b]) + team_2 = Node(title="Team B Lead", successors=[staff_c, staff_d]) + ceo = Node(title="CEO", successors=[team_1, team_2]) + + # Create a directed graph + G = nx.DiGraph() + + # Initialize position dictionary and label dictionary + pos = {} + labels = {} + + # Starting coordinates for the CEO + x_start = 10 + y_start = 10 + x_offset = 6 # Horizontal distance between nodes + y_offset = 2 # Vertical distance between each successor + + current_y = 0 + + # Function to recursively add nodes and edges to the graph + def add_nodes_recursive(graph, node, current_id, x, y, pos, labels) -> int: + # Add the current node to the graph + graph.add_node(current_id) + pos[current_id] = (x, y) + labels[current_id] = node.title + + # Add successors recursively + if node.successors: + for i, successor in enumerate(node.successors): + successor_id = len(pos) # Create a new unique ID for each successor + # Add edge from the current node to the successor + graph.add_edge(current_id, successor_id) + # Position each successor progressively lower + y = y - (i + 1) * y_offset # Adjust vertical spacing between successors + y = add_nodes_recursive(graph, successor, successor_id, x + x_offset, y, pos, + labels) # Adjust horizontal spacing + return y + + # Add CEO node and its successors recursively + add_nodes_recursive(G, ceo, 0, x_start, y_start, pos, labels) + + # Increase the canvas size using figsize (width, height in inches) + fig, ax = plt.subplots(figsize=(12, 8)) # Adjust this to make the canvas bigger + + # Define a function to manually position the arrows + def draw_edges_with_custom_arrows(graph, pos, ax): + for edge in graph.edges(): + start_node, end_node = edge + + # Get the positions of the nodes + start_x, start_y = pos[start_node] + end_x, end_y = pos[end_node] + + # Define the exit point (right of the start node) and entry point (left of the end node) + exit_x = start_x + 1.5 # Right side of the start node (assuming box width of 3) + entry_x = end_x - 1.5 # Left side of the end node (assuming box width of 3) + + # Draw the arrow + ax.annotate( + '', xy=(entry_x, end_y), xytext=(exit_x, start_y), + arrowprops=dict(arrowstyle='-|>', lw=1.5, color='black', + connectionstyle='arc3,rad=0.0') # Straight arrow + ) + + # Call the function to draw custom arrows + draw_edges_with_custom_arrows(G, pos, ax) + + # Draw the labels + nx.draw_networkx_labels(G, pos, labels, font_size=10, font_color="black") + + # Manually draw boxes (rectangles) around the nodes + for node, (x, y) in pos.items(): + # Define the size of each box (width and height can be adjusted) + width = 3 + height = 1.5 + # Draw a rectangle centered on the node's position + rect = Rectangle((x - width / 2, y - height / 2), width, height, + linewidth=1, edgecolor='black', facecolor='lightblue') + ax.add_patch(rect) + + # Dynamically calculate plot limits to ensure all boxes fit + x_values = [x for x, y in pos.values()] + y_values = [y for x, y in pos.values()] + + # Adjust the limits based on the positions and box sizes + x_margin = width / 2 + 1 # Add margin for the box width and extra space + y_margin = height / 2 + 1 # Add margin for the box height and extra space + + ax.set_xlim(min(x_values) - x_margin, max(x_values) + x_margin) + ax.set_ylim(min(y_values) - y_margin, max(y_values) + y_margin) + + # Remove the default axes for a cleaner look + ax.set_axis_off() + + # Add a title and display the plot + plt.title("Orgchart") + plt.savefig("test_successor_dag.png") + +if __name__ == "__main__": + pytest.main([__file__])