Skip to content

Structure cleanup and READMEs#9

Merged
kenobi314159 merged 4 commits intomainfrom
readme_cleanup
May 27, 2025
Merged

Structure cleanup and READMEs#9
kenobi314159 merged 4 commits intomainfrom
readme_cleanup

Conversation

@kenobi314159
Copy link
Owner

@kenobi314159 kenobi314159 commented May 27, 2025

PR Type

Enhancement, Documentation


Description

  • Added GUI for CI/CD graph visualization.

  • Implemented graph structure and drawing logic.

  • Updated README files with project details.

  • Added sample JSON input for graph generation.


Changes walkthrough 📝

Relevant files
Enhancement
cicd_graph.py
GUI Implementation for CI/CD Graph                                             

cicd_graph/cicd_graph.py

  • Added GUI class for graph visualization.
  • Implemented node and connection drawing methods.
  • Created graph structure classes for serial and parallel graphs.
  • [link]   
    input_test0.json
    Sample Input for Graph Generation                                               

    cicd_graph/input_test0.json

    • Added sample JSON input for graph generation.
    [link]   
    Documentation
    README.md
    Update Main README with Project Info                                         

    README.md

  • Expanded project description.
  • Listed project contents and features.
  • +7/-1     
    README.md
    Add README for CI/CD Graph Project                                             

    cicd_graph/README.md

    • Added detailed description of CI/CD graph project.
    +5/-0     
    README.md
    Add README for String Evolution Project                                   

    string_evolution/README.md

    • Added description of string evolution project.
    +11/-0   

    Need help?
  • Type /help how to ... in the comments thread for any questions about PR-Agent usage.
  • Check out the documentation for more information.
  • @kenobi314159 kenobi314159 self-assigned this May 27, 2025
    @kenobi314159 kenobi314159 merged commit f17369b into main May 27, 2025
    1 check passed
    @github-actions
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Code Complexity

    The guiGraphGrid class and its methods contain complex logic for drawing and connecting nodes. Consider simplifying the drawing logic or breaking it into smaller methods to enhance readability and maintainability.

    class guiGraphGrid:
        def __init__(self, fields_x, fields_y, field_size_x, field_size_y, title="Graph", background="white", border_width=0, border_color="black", padding_x=0, padding_y=0):
            self.fields_x = fields_x
            self.fields_y = fields_y
            self.field_size_x = field_size_x
            self.field_size_y = field_size_y
            self.padding_x = padding_x
            self.padding_y = padding_y
    
            self.window_root = tk.Tk()
            self.window_root.title(title)
            self.canvas = tk.Canvas(self.window_root, width = fields_x*field_size_x, height = fields_y*field_size_y)
            self.canvas.pack()
    
        def addNode(self, x, y, color="#cccccc", name="", text_size=12):
            x1 = int(x * self.field_size_x + self.padding_x)
            y1 = int(y * self.field_size_y + self.padding_y)
            x2 = int((x+1) * self.field_size_x - self.padding_x)
            y2 = int((y+1) * self.field_size_y - self.padding_y)
            radius = int(self.field_size_x * 0.1)
    
            points = [
                x1 + radius, y1,
                x1 + radius, y1,
                x2 - radius, y1,
                x2 - radius, y1,
                x2, y1,
                x2, y1 + radius,
                x2, y1 + radius,
                x2, y2 - radius,
                x2, y2 - radius,
                x2, y2,
                x2 - radius, y2,
                x2 - radius, y2,
                x1 + radius, y2,
                x1 + radius, y2,
                x1, y2,
                x1, y2 - radius,
                x1, y2 - radius,
                x1, y1 + radius,
                x1, y1 + radius,
                x1, y1,
            ]
            self.canvas.create_polygon(points, fill=color, smooth=True, outline="black", width=2)
    
            # Calculate the center of the rectangle
            center_x = (x1 + x2) / 2
            center_y = (y1 + y2) / 2
    
            # Add the text inside the rectangle
            self.canvas.create_text(center_x, center_y, text=name, fill="black", font=("Helvetica", text_size))
    
        def connectNodesBazier(self, from_x, from_y, to_x, to_y, through_x=-1, through_y=-1):
            x1 = int((from_x+1) * self.field_size_x - self.padding_x)
            y1 = int((from_y+0.5) * self.field_size_y)
            x2 = int(to_x * self.field_size_x + self.padding_x)
            y2 = int((to_y+0.5) * self.field_size_y)
            x3 = int((through_x+0.5) * self.field_size_x)
            y3 = int((through_y+0.5) * self.field_size_y)
    
            # Calculate control points for the Bezier curve
            control_x1 = x1 + (x2 - x1) / 3
            control_y1 = y1
            control_x2 = x1 + 2 * (x2 - x1) / 3
            control_y2 = y2
    
            # Create the curved line
            if (through_x == -1 or through_y == -1):
                self.canvas.create_line(x1, y1, control_x1, control_y1, control_x2, control_y2, x2, y2, smooth=True, width = 3)
            else:
                control_x1 = x1 + (x3 - x1) / 3
                control_y1 = y1
                control_x2 = x3 + 2 * (x2 - x3) / 3
                control_y2 = y2
                control_x3_in  = x1 + 2 * (x3 - x1) / 3
                control_y3_in  = y3
                control_x3_out = x3 + (x2 - x3) / 3
                control_y3_out = y3
                self.canvas.create_line(x1, y1, control_x1, control_y1, control_x3_in, control_y3_in, x3, y3, smooth=True, width = 3)
                self.canvas.create_line(x3, y3, control_x3_out, control_y3_out, control_x2, control_y2, x2, y2, smooth=True, width = 3)
    
            # Add arrowhead
            self.canvas.create_line(x2 - 10, y2 - 10, x2, y2, x2 - 10, y2 + 10, fill="black", width = 3)
    
        def connectNodesThreePart(self, from_x, from_y, to_x, to_y):
            x1 = int((from_x+1) * self.field_size_x - self.padding_x)
            y1 = int((from_y+0.5) * self.field_size_y)
            x2 = int(to_x * self.field_size_x + self.padding_x)
            y2 = int((to_y+0.5) * self.field_size_y)
    
            # Calculate the control points for the smooth bends
            vertical_x = int(to_x * self.field_size_x)
            vertical_y_dir = 1 if (from_y > to_y) else -1
            arc_rad = int(self.padding_x * 0.4)
            bend_x1 = vertical_x - arc_rad
            bend_y1 = y1
            bend_x2 = vertical_x
            bend_y2 = y1 - arc_rad * vertical_y_dir
            bend_x3 = vertical_x
            bend_y3 = y2 + arc_rad * vertical_y_dir
            bend_x4 = vertical_x + arc_rad
            bend_y4 = y2
    
            if (to_y == from_y):
                # Draw single line
                self.canvas.create_line(x1, y1, x2, y2, fill="black", width = 3)
            else:
                # Draw lines and bends
                self.canvas.create_line(x1, y1, bend_x1, bend_y1, fill="black", width = 3)
                self.canvas.create_arc(bend_x1 - arc_rad, bend_y1, bend_x2, bend_y2 - arc_rad * vertical_y_dir, start=315-45*vertical_y_dir, extent=90, style=tk.ARC, width = 3)
                self.canvas.create_line(bend_x2, bend_y2, bend_x3, bend_y3, fill="black", width = 3)
                self.canvas.create_arc(bend_x3, bend_y3 + arc_rad * vertical_y_dir, bend_x4 + arc_rad, bend_y4, start=135-45*vertical_y_dir, extent=90, style=tk.ARC, width = 3)
                self.canvas.create_line(bend_x4, bend_y4, x2, y2, fill="black", width = 3)
    
            # Add arrowhead
            self.canvas.create_line(x2 - 10, y2 - 10, x2, y2, x2 - 10, y2 + 10, fill="black", width = 3)
    
        def mainloop(self):
            self.window_root.mainloop()
    
    class graph:
        def __init__(self):
            pass
    
        def draw(self, grid, start_x, start_y):
            """
            Returns tuple (end_x, end_y, input_coords, output_coords)
            """
            return (start_x, start_y, [], [])
    
    class graphSimple(graph):
        def __init__(self, name):
            self.name = name
    
        def draw(self, grid, start_x, start_y):
            grid.addNode(start_x, start_y, name = self.name, color = "#77DE60")
            return (start_x + 1, start_y + 1, [[start_x, start_y]], [[start_x, start_y]])
    
    class graphSerial(graph):
        def __init__(self):
            self.sub_graphs = []
    
        def addStage(self, graph):
            assert (isinstance(graph, graphParallel) or isinstance(graph, graphSimple)), f"Serial graph may only contain simple or parallel sub-graphs"
            self.sub_graphs.append(graph)
    
        def draw(self, grid, start_x, start_y):
            x = start_x
            y = start_y
            end_y = start_y
            input_coords = []
            output_coords = []
            o_cs_pre = []
            for i,g in enumerate(self.sub_graphs):
                x, y, i_cs, o_cs = g.draw(grid, x, start_y)
                end_y = max(end_y, y)
                for o_c in o_cs_pre:
                    for i_c in i_cs:
                        grid.connectNodesThreePart(o_c[0], o_c[1], i_c[0], i_c[1])
                o_cs_pre = o_cs
                if (i == 0):
                    input_coords = i_cs
                if (i == len(self.sub_graphs)-1):
                    output_coords = o_cs
            end_x = x
            return (end_x, end_y, input_coords, output_coords)
    
    class graphParallel(graph):
        def __init__(self):
            self.sub_graphs = []
    
        def addStage(self, graph):
            assert (isinstance(graph, graphSerial) or isinstance(graph, graphSimple)), f"Parallel graph may only contain simple or serial sub-graphs"
            self.sub_graphs.append(graph)
    
        def draw(self, grid, start_x, start_y):
            x = start_x
            y = start_y
            end_x = start_x
            input_coords = []
            output_coords = []
            for i,g in enumerate(self.sub_graphs):
                x, y, i_cs, o_cs = g.draw(grid, start_x, y)
                end_x = max(end_x, x)
                input_coords += i_cs
                output_coords += o_cs
            end_y = y
            return (end_x, end_y, input_coords, output_coords)
    
    def getGraphFromJson(json_data, is_parallel=False):
        graph = graphParallel() if (is_parallel) else graphSerial()
        for k, v in json_data.items():
            if (len(k) >= 6 and k[:6] == "serial"):
                graph.addStage(getGraphFromJson(v, False))
            elif (len(k) >= 8 and k[:8] == "parallel"):
                graph.addStage(getGraphFromJson(v, True))
            elif (len(k) >= 5 and k[:5] == "stage"):
                graph.addStage(graphSimple(v))
        return graph
    
    if (__name__ == "__main__"):
        with open("input_test0.json", "r") as f:
            data = json.load(f)
    
        g = getGraphFromJson(data, True)
        grid = guiGraphGrid(14, 14, 200, 80, border_width=1, padding_x=40, padding_y=20)
        max_x, max_y, i_cs, o_cs = g.draw(grid, 0, 0)
    
        grid.mainloop()
    JSON Structure Validation

    The JSON structure used for graph generation should be validated to ensure it meets expected formats and constraints. Consider adding validation logic to handle incorrect structures gracefully.

    {
    	"serial0" : {
    		"stage0" : "start",
    		"stage1" : "start2",
    		"parallel0" : {
    			"stage0" : "test0",
    			"serial0" : {
    				"parallel0" : {
    					"stage0" : "circle0",
    					"stage1" : "circle1",
    					"stage2" : "circle2"
    				},
    				"parallel1" : {
    					"stage0" : "triangle0",
    					"serial0" : {
    						"stage0" : "triangle10",
    						"stage1" : "triangle11"
    					},
    					"stage1" : "triangle2",
    					"stage2" : "triangle3"
    				}
    			},
    			"stage1" : "test1",
    			"stage2" : "test2",
    			"serial1" : {
    				"stage0" : "small_test0",
    				"parallel0" : {
    					"stage0" : "hack0",
    					"stage1" : "hack1",
    					"stage2" : "hack2",
    					"stage3" : "hack3",
    					"stage4" : "hack4"
    				},
    				"stage1" : "small_test1"
    			}
    		},
    		"stage2" : "end"
    	}
    }

    @github-actions
    Copy link

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    General
    Improve assertion error messaging

    The assertion message could be misleading if the graph is of an unexpected type.
    Consider using a more descriptive error message that includes the type of the
    provided graph to aid in debugging.

    cicd_graph/cicd_graph.py [149-150]

    -assert (isinstance(graph, graphParallel) or isinstance(graph, graphSimple)), f"Serial graph may only contain simple or parallel sub-graphs"
    +assert (isinstance(graph, graphParallel) or isinstance(graph, graphSimple)), f"Serial graph may only contain simple or parallel sub-graphs, got {type(graph).__name__}"
    Suggestion importance[1-10]: 7

    __

    Why: The suggestion enhances the error message for better debugging by including the type of the provided graph. This improves maintainability and clarity in error handling.

    Medium
    Validate polygon points before drawing

    The create_polygon method is being called with a list of points that may not form a
    valid polygon if the points are not unique or are incorrectly defined. Ensure that
    the points list contains valid coordinates to avoid rendering issues. Consider
    adding validation for the points before drawing.

    cicd_graph/cicd_graph.py [48-49]

    -self.canvas.create_polygon(points, fill=color, smooth=True, outline="black", width=2)
    +if len(set(points)) >= 3:  # Ensure at least 3 unique points for a polygon
    +    self.canvas.create_polygon(points, fill=color, smooth=True, outline="black", width=2)
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion addresses potential rendering issues by ensuring that the points used to create a polygon are valid. However, it does not address the underlying logic of how points are defined.

    Low
    Clarify control point condition check

    The condition checks for through_x and through_y being -1, which may not be clear in
    intent. Consider using a more descriptive constant or enum to represent the absence
    of a control point for better readability and maintainability.

    cicd_graph/cicd_graph.py [73-74]

    -if (through_x == -1 or through_y == -1):
    +if (not self.has_control_point(through_x, through_y)):  # Define a method to check for control points
    Suggestion importance[1-10]: 5

    __

    Why: While the suggestion aims to improve readability by using a method to check for control points, it introduces a new method that is not defined in the provided code, which could lead to confusion.

    Low
    Ensure distinct arrowhead coordinates

    The arrowhead drawing logic could lead to overlapping lines if the coordinates are
    not calculated correctly. Ensure that the coordinates for the arrowhead are distinct
    and do not overlap with the line being drawn.

    cicd_graph/cicd_graph.py [121-122]

    -self.canvas.create_line(x2 - 10, y2 - 10, x2, y2, x2 - 10, y2 + 10, fill="black", width = 3)
    +arrow_head_coords = [(x2 - 10, y2 - 10), (x2, y2), (x2 - 10, y2 + 10)]
    +self.canvas.create_line(x2 - 10, y2 - 10, x2, y2, x2 - 10, y2 + 10, fill="black", width=3)
    Suggestion importance[1-10]: 4

    __

    Why: The suggestion points out a potential issue with overlapping lines in the arrowhead drawing logic. However, the proposed change does not alter the logic and merely reassigns coordinates without addressing the underlying calculation.

    Low

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    1 participant