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

few fixes #107

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

few fixes #107

wants to merge 1 commit into from

Conversation

hbmartin
Copy link
Owner

@hbmartin hbmartin commented Nov 26, 2024

Summary by Sourcery

Add a function to parse SVG path data and calculate bounding box dimensions, and refactor NodeFactory to use path elements instead of polygon elements for SVG parsing.

New Features:

  • Introduce a function to parse SVG path data and calculate bounding box dimensions.

Enhancements:

  • Refactor the NodeFactory to use path elements instead of polygon elements for SVG parsing.

Summary by CodeRabbit

  • New Features

    • Introduced new functions for parsing SVG path data, enhancing the ability to extract coordinates and dimensions.
    • Added support for parsing cluster elements in SVG data.
  • Bug Fixes

    • Improved handling of SVG elements, ensuring correct processing of paths and clusters.
  • Chores

    • Minor formatting adjustments across various files to enhance code readability.

Copy link
Contributor

sourcery-ai bot commented Nov 26, 2024

Reviewer's Guide by Sourcery

This PR implements several improvements to SVG path handling and geometric calculations in the graphviz2drawio converter. The main changes include adding SVG path parsing functionality, updating the node geometry calculations, and modifying how path data is extracted and processed.

Class diagram for updated SVG path handling

classDiagram
    class SVG {
        +parse_svg_path(path_data: str) float[]
        +get_d(g: Element) str
    }
    class MxGraph {
        +add_mx_geo(element, text_offset)
    }
    class NodeFactory {
        +from_svg(g: Element, gradients)
    }
    class Rect {
        +x: float
        +y: float
        +width: float
        +height: float
    }
    SVG --> Rect
    NodeFactory --> SVG
    MxGraph --> SVG
    note for SVG "New method parse_svg_path added for path parsing"
    note for NodeFactory "Updated to use path instead of polygon for geometry"
Loading

File-Level Changes

Change Details Files
Added new SVG path parsing functionality
  • Implemented parse_svg_path function to extract coordinates from path data
  • Added functionality to calculate bounding box dimensions
  • Added function to compute center coordinates from path data
graphviz2drawio/models/SVG.py
Updated node geometry handling in MxGraph
  • Modified text offset calculations to include width and height
  • Updated geometry attributes calculation for better positioning
  • Simplified geometry element creation by removing nested point element
graphviz2drawio/mx/MxGraph.py
Modified node factory to handle path elements instead of polygons
  • Updated SVG parsing to work with path elements instead of polygons
  • Changed coordinate extraction to use new path parsing functions
  • Simplified rectangle creation using direct coordinate values
graphviz2drawio/mx/NodeFactory.py
Added path data extraction functionality
  • Implemented get_d function to extract path data using regex
  • Added coordinate and dimension parsing from path strings
  • Integrated path parsing with existing SVG processing
graphviz2drawio/models/SVG.py

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

coderabbitai bot commented Nov 26, 2024

Walkthrough

This pull request introduces several changes across multiple files in the graphviz2drawio project. Key updates include minor formatting adjustments in the convert function of graphviz2drawio.py, the addition of new SVG path parsing functionality in SVG.py, enhancements to SVG element handling in SvgParser.py, modifications to the MxGraph class to incorporate clustering in MxGraph.py, and significant changes to the NodeFactory class to handle SVG paths in NodeFactory.py. Overall, the changes improve SVG parsing and node management without altering existing core functionalities.

Changes

File Path Change Summary
graphviz2drawio/graphviz2drawio.py Removed trailing whitespace in the convert function.
graphviz2drawio/models/SVG.py Added parse_svg_path and get_d functions for SVG path data parsing and dimension calculation.
graphviz2drawio/models/SvgParser.py Updated parse_nodes_edges_clusters to handle SVG clusters; added import for ElementTree.
graphviz2drawio/mx/MxGraph.py Modified __init__ to accept clusters, updated add_mx_geo for geometry calculations.
graphviz2drawio/mx/NodeFactory.py Changed from_svg to handle path elements, added import for Rect.

Possibly related PRs

🐰 In the world of SVG, paths now shine,
With clusters and nodes, all neatly aligned.
From whitespace to parsing, our code takes a leap,
In the garden of graphs, new secrets we keep.
Hopping through changes, we cheer with delight,
For every small tweak makes our project feel right! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @hbmartin - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please provide a more descriptive PR title that reflects the actual changes (SVG path parsing and geometry calculation improvements)
  • Add error handling for edge cases in parse_svg_path() - what happens with malformed path data or odd number of coordinates?
  • Consider adding unit tests for the new SVG parsing and geometry calculation functions
Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -26,6 +51,19 @@ def get_text(g: Element) -> str | None:
return text_el.text
return None

def get_d(g: Element) -> str | None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider using direct Element attribute access instead of string conversion and regex parsing

The get_d function introduces unnecessary complexity by converting the XML Element back to a string and using regex to parse it. The Element API provides direct access to attributes, which would simplify this code significantly.

Instead of:

def get_d(g: Element) -> str | None:
    polygon_str = ET.tostring(g, encoding='unicode')
    polygon_elements = re.findall(r'<ns0:path[^>]*>', polygon_str)
    polygon_str = ''.join(polygon_elements)
    match = re.search(r'd="([^"]+)"', polygon_str)
    if match:
        points = match.group(1)
        x_coords, y_coords, width, height = parse_svg_path(points)
        return x_coords, y_coords, width, height

Use:

def get_d(g: Element) -> tuple[float, float, float, float] | None:
    if path_data := g.get('d'):
        return parse_svg_path(path_data)
    return None

This maintains the same functionality while being more direct and efficient. It also properly types the return value as a tuple of coordinates and dimensions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Outside diff range comments (2)
graphviz2drawio/models/SVG.py (1)

Line range hint 1-66: Consider standardizing XML parsing approach.

The module currently mixes different XML parsing strategies (ElementTree API and string manipulation). Consider standardizing on ElementTree's API throughout the codebase for better maintainability and reliability.

🧰 Tools
🪛 Ruff (0.8.0)

8-8: Missing return type annotation for public function parse_svg_path

(ANN201)


8-8: Missing type annotation for function argument path_data

(ANN001)


10-10: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)

graphviz2drawio/models/SvgParser.py (1)

Line range hint 71-74: Consider adding cluster-specific validation

The cluster handling implementation looks good, but consider adding validation for cluster-specific attributes and support for nested clusters.

Consider adding:

  1. Validation of cluster-specific attributes (e.g., style, penwidth)
  2. Support for nested clusters by recursively processing child elements
  3. Error handling for malformed cluster definitions

Would you like me to provide an example implementation with these improvements?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

📥 Commits

Reviewing files that changed from the base of the PR and between 4a40372 and 51b33bd.

📒 Files selected for processing (5)
  • graphviz2drawio/graphviz2drawio.py (2 hunks)
  • graphviz2drawio/models/SVG.py (2 hunks)
  • graphviz2drawio/models/SvgParser.py (2 hunks)
  • graphviz2drawio/mx/MxGraph.py (4 hunks)
  • graphviz2drawio/mx/NodeFactory.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.8.0)
graphviz2drawio/graphviz2drawio.py

16-16: Blank line contains whitespace

Remove whitespace from blank line

(W293)


25-25: Blank line contains whitespace

Remove whitespace from blank line

(W293)

graphviz2drawio/models/SVG.py

8-8: Missing return type annotation for public function parse_svg_path

(ANN201)


8-8: Missing type annotation for function argument path_data

(ANN001)


10-10: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


55-55: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


57-57: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


60-60: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


63-66: Missing explicit return at the end of function able to return non-None value

Add explicit return statement

(RET503)

graphviz2drawio/models/SvgParser.py

18-18: xml.etree.ElementTree imported but unused

Remove unused import: xml.etree.ElementTree

(F401)

graphviz2drawio/mx/MxGraph.py

11-11: graphviz2drawio.models.SVG imported but unused

Remove unused import: graphviz2drawio.models.SVG

(F401)


20-20: Blank line contains whitespace

Remove whitespace from blank line

(W293)


110-110: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Local variable geo is assigned to but never used

Remove assignment to unused variable geo

(F841)


121-121: Trailing comma missing

Add trailing comma

(COM812)


174-174: No newline at end of file

Add trailing newline

(W292)

🔇 Additional comments (5)
graphviz2drawio/graphviz2drawio.py (1)

Line range hint 1-42: LGTM! Changes are purely formatting-related

The changes only address whitespace issues and don't affect the core functionality. The overall code structure remains clean and well-organized with proper error handling and type hints.

🧰 Tools
🪛 Ruff (0.8.0)

16-16: Blank line contains whitespace

Remove whitespace from blank line

(W293)

graphviz2drawio/models/SvgParser.py (1)

Line range hint 23-24: Verify cluster handling in callers

The function returns clusters as part of its tuple return value. Let's verify that all callers handle this correctly.

✅ Verification successful

Let me gather more information about how the clusters are handled in MxGraph.py since that's where the parsed clusters would be used.


Clusters are properly handled throughout the codebase

The verification shows that:

  1. In graphviz2drawio.py, clusters are correctly unpacked from the parse_nodes_edges_clusters return value
  2. In MxGraph.py, clusters are:
    • Properly typed as OrderedDict[str, Node] in the constructor
    • Correctly processed before nodes and edges using the same add_node method
    • Rendered in the correct order to maintain proper visual layering
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find all callers of parse_nodes_edges_clusters and verify they handle clusters
rg -l "parse_nodes_edges_clusters" | while read -r file; do
  echo "=== $file ==="
  # Show the function calls with context to verify cluster handling
  rg -B 2 -A 2 "parse_nodes_edges_clusters" "$file"
done

Length of output: 797


Script:

#!/bin/bash
# Check how clusters are handled in MxGraph.py
ast-grep --pattern 'def __init__($$$, clusters: $_, $$$)'  # Check constructor
rg -B 2 -A 5 "clusters" graphviz2drawio/mx/MxGraph.py     # Check cluster usage

Length of output: 642

🧰 Tools
🪛 Ruff (0.8.0)

18-18: xml.etree.ElementTree imported but unused

Remove unused import: xml.etree.ElementTree

(F401)

graphviz2drawio/mx/MxGraph.py (2)

Line range hint 14-33: LGTM! Well-structured constructor changes.

The constructor changes properly implement cluster support while maintaining good practices:

  • Correct type hints with OrderedDict
  • Logical ordering (clusters before nodes)
  • Clean integration with existing node handling
🧰 Tools
🪛 Ruff (0.8.0)

20-20: Blank line contains whitespace

Remove whitespace from blank line

(W293)


112-122: ⚠️ Potential issue

Fix unused variable and verify geometry calculations.

  1. The geo variable is assigned but never used
  2. The geometry calculations need validation:
    • Are the width/height calculations producing correct dimensions?
    • Is dividing offset by 2 for x/y coordinates the intended behavior?
✅ Verification successful

The previous search patterns were too specific. Let's try broader patterns to understand the geometry calculations and their usage in the codebase.


The geo variable usage and geometry calculations are correct

  1. The geo variable in add_mx_geo is not unused - it's correctly used as the parent element for potential child elements in similar pattern as seen in add_mx_geo_with_points.

  2. The geometry calculations are consistent with the codebase:

    • Width/height calculations using 0.25 factor are intentional for text offset scaling
    • Dividing x/y by 2 for coordinates is consistent with the text positioning logic
    • The code has test coverage for geometry in test/test_rect.py
    • Similar geometry handling patterns are used across NodeFactory.py, RectFactory.py, and SVG.py
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other geometry calculations in the codebase for consistency
rg -l "as.*geometry.*width.*height" --type py

# Look for tests validating these calculations
rg -l "test.*geo.*width.*height" --type py

Length of output: 92


Script:

#!/bin/bash
# Search for geometry-related code with more flexible patterns
rg -A 5 "geometry" --type py

# Look for width/height calculations
rg -A 5 "width.*height" --type py

# Search for the specific file and show context
cat graphviz2drawio/mx/MxGraph.py

Length of output: 10684

🧰 Tools
🪛 Ruff (0.8.0)

112-112: Local variable geo is assigned to but never used

Remove assignment to unused variable geo

(F841)


121-121: Trailing comma missing

Add trailing comma

(COM812)

graphviz2drawio/mx/NodeFactory.py (1)

14-14: Import Rect class for rectangle creation

The Rect class is now imported correctly, which is necessary for creating Rect objects in the updated code.

@@ -13,7 +13,7 @@ def convert(graph_to_convert: AGraph | str | IO, layout_prog: str = "dot") -> st
graph = graph_to_convert
else:
graph = AGraph(graph_to_convert)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Remove trailing whitespace from blank line

While the blank line serves a good purpose separating logical blocks, it should not contain whitespace characters.

-    
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
🧰 Tools
🪛 Ruff (0.8.0)

16-16: Blank line contains whitespace

Remove whitespace from blank line

(W293)

@@ -22,7 +22,7 @@ def convert(graph_to_convert: AGraph | str | IO, layout_prog: str = "dot") -> st
graph_nodes: dict[str, dict] = {n: n.attr.to_dict() for n in graph.nodes_iter()}

svg_graph = graph.draw(prog=layout_prog, format="svg")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Remove trailing whitespace from blank line

While the blank line provides good visual separation between initialization and processing, it should not contain whitespace characters.

-    
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
🧰 Tools
🪛 Ruff (0.8.0)

25-25: Blank line contains whitespace

Remove whitespace from blank line

(W293)

Comment on lines +8 to +28
def parse_svg_path(path_data):
# Extract all numbers from the path data
numbers = list(map(float, re.findall(r'-?\d+\.?\d*', path_data)))

# Separate x and y coordinates
x_coords = numbers[0::2] # Every other value starting from index 0
y_coords = numbers[1::2] # Every other value starting from index 1
# Calculate the bounding box
min_x = min(x_coords)
max_x = max(x_coords)
min_y = min(y_coords)
max_y = max(y_coords)

width = max_x - min_x
height = max_y - min_y

# Single x and y as the center of the bounding box
single_x = (min_x + max_x) / 2
single_y = (min_y + max_y) / 2

return single_x, single_y, width, height
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type hints and error handling to improve robustness.

The function needs several improvements for better maintainability and reliability:

-def parse_svg_path(path_data):
+def parse_svg_path(path_data: str) -> tuple[float, float, float, float]:
+    """Parse SVG path data and return center coordinates with dimensions.
+    
+    Args:
+        path_data: SVG path data string containing coordinate values
+        
+    Returns:
+        tuple of (center_x, center_y, width, height)
+        
+    Raises:
+        ValueError: If path_data is empty or contains invalid numbers
+    """
     # Extract all numbers from the path data
-    numbers = list(map(float, re.findall(r'-?\d+\.?\d*', path_data)))
+    if not path_data:
+        raise ValueError("Empty path data")
+        
+    number_matches = re.findall(r'-?\d+\.?\d*(?:e-?\d+)?', path_data)
+    if not number_matches:
+        raise ValueError("No valid numbers found in path data")
+        
+    try:
+        numbers = [float(n) for n in number_matches]
+    except ValueError as e:
+        raise ValueError(f"Invalid number in path data: {e}")

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.8.0)

8-8: Missing return type annotation for public function parse_svg_path

(ANN201)


8-8: Missing type annotation for function argument path_data

(ANN001)


10-10: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)

Comment on lines +54 to +66
def get_d(g: Element) -> str | None:
polygon_str = ET.tostring(g, encoding='unicode')
# Regular expression to find the <ns0:polygon> tag
polygon_elements = re.findall(r'<ns0:path[^>]*>', polygon_str)

# Join the results (in case there are multiple matches)
polygon_str = ''.join(polygon_elements)
match = re.search(r'd="([^"]+)"', polygon_str)
# If a match is found, format it as a dictionary
if match:
points = match.group(1)
x_coords, y_coords, width, height = parse_svg_path(points)
return x_coords, y_coords, width, height
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

⚠️ Potential issue

Refactoring suggestion needs adjustment

The review comment's suggested changes need revision based on the verification results:

  1. The function get_d is currently used in NodeFactory.py where it correctly unpacks 4 values (x_coords, y_coords, width, height) and the return values are used to create a Rect object.
  2. The current return type annotation str | None is incorrect as the function actually returns a tuple of coordinates and dimensions.
  3. The function name get_d should remain as is since it refers to the SVG path's 'd' attribute, following SVG terminology.
def get_d(g: Element) -> tuple[float, float, float, float] | None:
    """Extract coordinates and dimensions from an SVG path element's 'd' attribute.
    
    Args:
        g: SVG Element containing a path
        
    Returns:
        tuple of (x_coords, y_coords, width, height) or None if no path found
    """
-    polygon_str = ET.tostring(g, encoding='unicode')
-    # Regular expression to find the <ns0:polygon> tag
-    polygon_elements = re.findall(r'<ns0:path[^>]*>', polygon_str)
-
-    # Join the results (in case there are multiple matches)
-    polygon_str = ''.join(polygon_elements)
-    match = re.search(r'd="([^"]+)"', polygon_str)
-    # If a match is found, format it as a dictionary
-    if match:
-        points = match.group(1)
-        x_coords, y_coords, width, height = parse_svg_path(points)
+    path = g.find(f".//{NS_SVG}path")
+    if path is not None and "d" in path.attrib:
+        points = path.attrib["d"]
+        try:
+            return parse_svg_path(points)
+        except ValueError as e:
+            logging.warning(f"Failed to parse path data: {e}")
+            return None
+    return None

The main issues to address are:

  • Fix the incorrect return type annotation
  • Add proper docstring
  • Use XML parsing instead of string manipulation
  • Add error handling for parse_svg_path
  • Keep the original function name as it's domain-specific
🔗 Analysis chain

Refactor for robustness and clarity.

The current implementation has several issues:

  1. Converting XML to string for parsing is fragile and inefficient
  2. Function name doesn't reflect its return value
  3. Missing type hints and error handling
-def get_d(g: Element) -> str | None:
+def get_path_dimensions(g: Element) -> tuple[float, float, float, float] | None:
+    """Extract center coordinates and dimensions from an SVG path element.
+    
+    Args:
+        g: SVG Element containing a path
+        
+    Returns:
+        tuple of (center_x, center_y, width, height) or None if no path found
+    """
-    polygon_str = ET.tostring(g, encoding='unicode')
-    # Regular expression to find the <ns0:polygon> tag
-    polygon_elements = re.findall(r'<ns0:path[^>]*>', polygon_str)
-
-    # Join the results (in case there are multiple matches)
-    polygon_str = ''.join(polygon_elements)
-    match = re.search(r'd="([^"]+)"', polygon_str)
-    # If a match is found, format it as a dictionary
-    if match:
-        points = match.group(1)
-        x_coords, y_coords, width, height = parse_svg_path(points)
+    path = g.find(f".//{NS_SVG}path")
+    if path is not None and "d" in path.attrib:
+        points = path.attrib["d"]
+        try:
+            return parse_svg_path(points)
+        except ValueError as e:
+            logging.warning(f"Failed to parse path data: {e}")
+            return None
+    return None

Also, consider verifying the impact of these changes on the codebase:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find all usages of get_d to ensure they handle the new return type
rg -l "get_d\(" | xargs ast-grep --pattern 'get_d($$$)'

Length of output: 57


Script:

#!/bin/bash
# Search for get_d function calls using ripgrep with context
rg -A 3 "get_d\(" 

Length of output: 719


Script:

#!/bin/bash
# Search for the function definition to verify its location
rg -A 5 "^def get_d"

Length of output: 475


Script:

#!/bin/bash
# Find all Python files that might import or use SVG.py
fd ".py$" | xargs rg "from.*SVG import|import.*SVG"

Length of output: 331


Script:

#!/bin/bash
# Check the usage context in NodeFactory.py to understand how the return values are used
rg -B 5 -A 10 "get_d\(" graphviz2drawio/mx/NodeFactory.py

Length of output: 933


Script:

#!/bin/bash
# Look for parse_svg_path function to understand the return type contract
rg -A 5 "def parse_svg_path" 

Length of output: 464

🧰 Tools
🪛 Ruff (0.8.0)

55-55: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


57-57: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


60-60: Single quotes found but double quotes preferred

Replace single quotes with double quotes

(Q000)


63-66: Missing explicit return at the end of function able to return non-None value

Add explicit return statement

(RET503)

@@ -68,6 +67,7 @@ def parse_nodes_edges_clusters(
else:
edges[edge.key_for_label] = edge
elif g.attrib["class"] == "cluster":

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Remove unnecessary empty line

This empty line is not needed and affects code readability.

@@ -15,6 +15,7 @@
from .commented_tree_builder import COMMENT, CommentedTreeBuilder
from .CoordsTranslate import CoordsTranslate
from .Errors import MissingTitleError
import xml.etree.ElementTree as ET
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove redundant import

This import is redundant as ElementTree is already imported on line 5. Additionally, the ET alias is never used in the code.

Apply this diff to remove the redundant import:

-import xml.etree.ElementTree as ET
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import xml.etree.ElementTree as ET
🧰 Tools
🪛 Ruff (0.8.0)

18-18: xml.etree.ElementTree imported but unused

Remove unused import: xml.etree.ElementTree

(F401)

@@ -167,4 +171,4 @@ def __str__(self) -> str:
return self.value()

def __repr__(self) -> str:
return self.value()
return self.value()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add newline at end of file.

Add a trailing newline to follow Python coding standards.

-        return self.value()
+        return self.value()
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return self.value()
return self.value()
🧰 Tools
🪛 Ruff (0.8.0)

174-174: No newline at end of file

Add trailing newline

(W292)

@@ -8,6 +8,7 @@
from graphviz2drawio.mx.Edge import Edge
from graphviz2drawio.mx.Node import Node
from graphviz2drawio.mx.Styles import Styles
from graphviz2drawio.models import SVG
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Remove unused import.

The SVG import from graphviz2drawio.models is not used in this file.

-from graphviz2drawio.models import SVG
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from graphviz2drawio.models import SVG
🧰 Tools
🪛 Ruff (0.8.0)

11-11: graphviz2drawio.models.SVG imported but unused

Remove unused import: graphviz2drawio.models.SVG

(F401)

Comment on lines +108 to +109
width = abs(text_offset.real) * 0.25 # Example calculation
height = abs(text_offset.imag) * 0.25 # Example calculation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Document or refactor magic numbers.

The width and height calculations use a magic number (0.25) without explanation. Consider extracting this as a named constant or documenting why this specific scaling factor is used.

-            width = abs(text_offset.real) * 0.25  # Example calculation
-            height = abs(text_offset.imag) * 0.25  # Example calculation
+            SCALING_FACTOR = 0.25  # Factor for converting text offset to dimensions
+            width = abs(text_offset.real) * SCALING_FACTOR
+            height = abs(text_offset.imag) * SCALING_FACTOR
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
width = abs(text_offset.real) * 0.25 # Example calculation
height = abs(text_offset.imag) * 0.25 # Example calculation
SCALING_FACTOR = 0.25 # Factor for converting text offset to dimensions
width = abs(text_offset.real) * SCALING_FACTOR
height = abs(text_offset.imag) * SCALING_FACTOR

Comment on lines +40 to +42
if (polygon := SVG.get_first(g, "path")) is not None:
x_coords, y_coords, width, height = SVG.get_d(polygon)
rect = Rect(x=x_coords,y=y_coords,width=width,height=height)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Rename variable polygon to path_element for clarity

The variable polygon is assigned a path element from the SVG. To improve code readability and prevent confusion, consider renaming polygon to path_element.

Apply this diff to rename the variable:

-if (polygon := SVG.get_first(g, "path")) is not None:
-    x_coords, y_coords, width, height = SVG.get_d(polygon)
-    rect = Rect(x=x_coords, y=y_coords, width=width, height=height)
+if (path_element := SVG.get_first(g, "path")) is not None:
+    x_coords, y_coords, width, height = SVG.get_d(path_element)
+    rect = Rect(x=x_coords, y=y_coords, width=width, height=height)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (polygon := SVG.get_first(g, "path")) is not None:
x_coords, y_coords, width, height = SVG.get_d(polygon)
rect = Rect(x=x_coords,y=y_coords,width=width,height=height)
if (path_element := SVG.get_first(g, "path")) is not None:
x_coords, y_coords, width, height = SVG.get_d(path_element)
rect = Rect(x=x_coords,y=y_coords,width=width,height=height)

@khushalcodiste
Copy link

Any updates on this implementation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants