| | 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, output_format: 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. |
| | |
| | output_format (str): The output format for the generated diagram. |
| | Supported formats: "png" or "svg" |
| | |
| | Expected JSON Format Example: |
| | { |
| | "central_node": "AI Project Lifecycle", |
| | "nodes": [ |
| | { |
| | "id": "phase1", |
| | "label": "I. Problem Definition & Data Acquisition", |
| | "relationship": "Starts with", |
| | "subnodes": [ |
| | { |
| | "id": "sub1_1", |
| | "label": "1. Problem Formulation", |
| | "relationship": "Involves", |
| | "subnodes": [ |
| | {"id": "sub1_1_1", "label": "1.1. Identify Business Need", "relationship": "e.g."}, |
| | {"id": "sub1_1_2", "label": "1.2. Define KPIs", "relationship": "e.g."} |
| | ] |
| | }, |
| | { |
| | "id": "sub1_2", |
| | "label": "2. Data Collection", |
| | "relationship": "Followed by", |
| | "subnodes": [ |
| | {"id": "sub1_2_1", "label": "2.1. Source Data", "relationship": "from"}, |
| | {"id": "sub1_2_2", "label": "2.2. Data Cleaning", "relationship": "includes"} |
| | ] |
| | } |
| | ] |
| | }, |
| | { |
| | "id": "phase2", |
| | "label": "II. Model Development", |
| | "relationship": "Proceeds to", |
| | "subnodes": [ |
| | { |
| | "id": "sub2_1", |
| | "label": "1. Feature Engineering", |
| | "relationship": "Comprises", |
| | "subnodes": [ |
| | {"id": "sub2_1_1", "label": "1.1. Feature Selection", "relationship": "e.g."}, |
| | {"id": "sub2_1_2", "label": "1.2. Feature Transformation", "relationship": "e.g."} |
| | ] |
| | }, |
| | { |
| | "id": "sub2_2", |
| | "label": "2. Model Training", |
| | "relationship": "Involves", |
| | "subnodes": [ |
| | {"id": "sub2_2_1", "label": "2.1. Algorithm Selection", "relationship": "uses"}, |
| | {"id": "sub2_2_2", "label": "2.2. Hyperparameter Tuning", "relationship": "optimizes"} |
| | ] |
| | } |
| | ] |
| | }, |
| | { |
| | "id": "phase3", |
| | "label": "III. Evaluation & Deployment", |
| | "relationship": "Culminates in", |
| | "subnodes": [ |
| | { |
| | "id": "sub3_1", |
| | "label": "1. Model Evaluation", |
| | "relationship": "Includes", |
| | "subnodes": [ |
| | {"id": "sub3_1_1", "label": "1.1. Performance Metrics", "relationship": "measures"}, |
| | {"id": "sub3_1_2", "label": "1.2. Bias & Fairness Audits", "relationship": "ensures"} |
| | ] |
| | }, |
| | { |
| | "id": "sub3_2", |
| | "label": "2. Deployment & Monitoring", |
| | "relationship": "Requires", |
| | "subnodes": [ |
| | {"id": "sub3_2_1", "label": "2.1. API/Integration Development", "relationship": "for"}, |
| | {"id": "sub3_2_2", "label": "2.2. Continuous Monitoring", "relationship": "ensures"} |
| | ] |
| | } |
| | ] |
| | } |
| | ] |
| | } |
| | |
| | Returns: |
| | str: The filepath to the generated image file. |
| | """ |
| | 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' |
| | } |
| | ) |
| | |
| | |
| | base_color = '#BEBEBE' |
| |
|
| | dot.node( |
| | 'central', |
| | data['central_node'], |
| | shape='box', |
| | style='filled,rounded', |
| | fillcolor=base_color, |
| | fontcolor='black', |
| | fontsize='16' |
| | ) |
| | |
| | add_nodes_and_edges(dot, 'central', data.get('nodes', []), current_depth=1, base_color=base_color) |
| |
|
| | with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp: |
| | dot.render(tmp.name, format=output_format, cleanup=True) |
| | return f"{tmp.name}.{output_format}" |
| |
|
| | except json.JSONDecodeError: |
| | return "Error: Invalid JSON format" |
| | except Exception as e: |
| | return f"Error: {str(e)}" |
| |
|
| |
|