Spaces:
Running
Running
| 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. | |
| 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 PNG 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") | |
| # 한글 폰트 설정 | |
| # GDFONTPATH가 설정되어 있으면 폰트 파일명(확장자 제외) 사용 | |
| korean_font = 'NanumGothic-Regular' | |
| dot = graphviz.Digraph( | |
| name='SynopticChart', | |
| format='png', | |
| graph_attr={ | |
| 'rankdir': 'LR', # Left-to-Right layout (horizontal hierarchy) | |
| 'splines': 'ortho', # Straight lines | |
| 'bgcolor': 'white', # White background | |
| 'pad': '0.5', # Padding around the graph | |
| 'ranksep': '0.7', # Reduced horizontal separation between ranks (columns) | |
| 'nodesep': '0.3', # Adjusted vertical separation between nodes in the same rank | |
| 'fontname': korean_font, # 그래프 전체 한글 폰트 | |
| 'charset': 'UTF-8' # UTF-8 인코딩 | |
| }, | |
| node_attr={ | |
| 'fontname': korean_font # 모든 노드의 기본 폰트 | |
| }, | |
| edge_attr={ | |
| 'fontname': korean_font # 모든 엣지의 기본 폰트 | |
| } | |
| ) | |
| base_color = '#19191a' | |
| dot.node( | |
| 'central', | |
| data['central_node'], | |
| shape='box', # Rectangular shape | |
| style='filled,rounded', # Filled and rounded corners | |
| fillcolor=base_color, # Darkest color | |
| fontcolor='white', # White text for dark background | |
| fontsize='16', # Larger font for central node | |
| fontname=korean_font # 한글 폰트 명시적 지정 | |
| ) | |
| 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)}" |