| import graphviz |
| import json |
| from tempfile import NamedTemporaryFile |
| import os |
| from graph_generator_utils import add_nodes_and_edges |
|
|
| def generate_synoptic_chart(json_input: str, base_color: str) -> str: |
| """ |
| Generates a synoptic chart (horizontal flowchart) from JSON input. |
| |
| Args: |
| json_input (str): A JSON string describing the synoptic chart structure. |
| It must follow the Expected JSON Format Example below. |
| base_color (str): The hexadecimal color string (e.g., '#19191a') for the base |
| color of the nodes, from which a gradient will be generated. |
| |
| Returns: |
| str: The filepath to the generated PNG image file. |
| |
| Expected JSON Format Example: |
| { |
| "central_node": "AI Project Lifecycle", |
| "nodes": [ |
| { |
| "id": "phase1", |
| "label": "I. Problem Definition & Data Acquisition", |
| "relationship": "Starts\\nwith", |
| "subnodes": [ |
| { |
| "id": "sub1_1", |
| "label": "1. Problem Formulation", |
| "relationship": "Involves\\n(Steps)", |
| "subnodes": [ |
| {"id": "sub1_1_1", "label": "1.1. Identify Business Need", "relationship": "e.g.\\n(Need)"}, |
| {"id": "sub1_1_2", "label": "1.2. Define KPIs", "relationship": "e.g.\\n(Metrics)"} |
| ] |
| } |
| ] |
| }, |
| { |
| "id": "phase2", |
| "label": "II. Model Development", |
| "relationship": "Proceeds\\nto", |
| "subnodes": [ |
| { |
| "id": "sub2_1", |
| "label": "1. Feature Engineering", |
| "relationship": "Comprises\\n(Features)", |
| "subnodes": [ |
| {"id": "sub2_1_1", "label": "1.1. Feature Selection", "relationship": "e.g.\\n(Select)"} |
| ] |
| } |
| ] |
| } |
| ] |
| } |
| """ |
| try: |
| if not json_input.strip(): |
| return "Error: Empty input" |
| |
| data = json.loads(json_input) |
| |
| if 'central_node' not in data or 'nodes' not in data: |
| raise ValueError("Missing required fields: central_node or nodes") |
|
|
| dot = graphviz.Digraph( |
| name='SynopticChart', |
| format='png', |
| graph_attr={ |
| 'rankdir': 'LR', |
| 'splines': 'ortho', |
| 'bgcolor': 'white', |
| 'pad': '0.5', |
| 'ranksep': '0.7', |
| 'nodesep': '0.3' |
| } |
| ) |
| |
| |
| if not isinstance(base_color, str) or not base_color.startswith('#') or len(base_color) != 7: |
| base_color = '#19191a' |
|
|
| base_color = '#19191a' |
|
|
| |
| dot.node( |
| 'central', |
| data['central_node'], |
| shape='box', |
| style='filled,rounded', |
| fillcolor=base_color, |
| fontcolor='white', |
| fontsize='16' |
| ) |
| |
| |
| add_nodes_and_edges(dot, 'central', data.get('nodes', []), current_depth=1, base_color=base_color) |
|
|
| |
| with NamedTemporaryFile(delete=False, suffix='.png') as tmp: |
| dot.render(tmp.name, format='png', cleanup=True) |
| return tmp.name + '.png' |
|
|
| except json.JSONDecodeError: |
| return "Error: Invalid JSON format" |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
|
|