Graphify / app.py
ZahirJS's picture
Update app.py
89c005a verified
raw
history blame
6.51 kB
# app.py
import gradio as gr
import json
from graphviz import Digraph
import os
from tempfile import NamedTemporaryFile
from sample_data import COMPLEX_SAMPLE_JSON
def generate_concept_map(json_input: str) -> str:
"""
Generate concept map from JSON and return as image file
Args:
json_input (str): JSON describing the concept map structure.
REQUIRED FORMAT EXAMPLE:
{
"central_node": "AI",
"nodes": [
{
"id": "ml",
"label": "Machine Learning",
"relationship": "subcategory",
"subnodes": [
{
"id": "dl",
"label": "Deep Learning",
"relationship": "type",
"subnodes": [
{
"id": "cnn",
"label": "CNN",
"relationship": "architecture"
}
]
}
]
}
]
}
Returns:
str: Path to 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")
# Create graph
dot = Digraph(
name='ConceptMap',
format='png',
graph_attr={
'rankdir': 'TB',
'splines': 'ortho',
'bgcolor': 'transparent'
}
)
# Central node (ellipse)
dot.node(
'central',
data['central_node'],
shape='ellipse',
style='filled',
fillcolor='#2196F3',
fontcolor='white',
fontsize='14'
)
# Process nodes (rectangles)
for node in data['nodes']:
node_id = node.get('id')
label = node.get('label')
relationship = node.get('relationship')
# Validate node
if not all([node_id, label, relationship]):
raise ValueError(f"Invalid node: {node}")
# Create main node (rectangle)
dot.node(
node_id,
label,
shape='box',
style='filled',
fillcolor='#4CAF50',
fontcolor='white',
fontsize='12'
)
# Connect to central node
dot.edge(
'central',
node_id,
label=relationship,
color='#9C27B0',
fontsize='10'
)
# Helper function to recursively add subnodes and edges
def add_subnodes(parent_id, subnodes_list, fill_color, font_size, edge_color, edge_font_size):
for subnode in subnodes_list:
sub_id = subnode.get('id')
sub_label = subnode.get('label')
sub_rel = subnode.get('relationship')
if not all([sub_id, sub_label, sub_rel]):
raise ValueError(f"Invalid subnode: {subnode}")
dot.node(
sub_id,
sub_label,
shape='box',
style='filled',
fillcolor=fill_color,
fontcolor='white',
fontsize=str(font_size)
)
dot.edge(
parent_id,
sub_id,
label=sub_rel,
color=edge_color,
fontsize=str(edge_font_size)
)
# Recursively call for deeper levels
if 'subnodes' in subnode:
# Slightly adjust colors/sizes for deeper levels if desired
# For fixed 2 children per parent, you might keep colors consistent per level or vary them.
# Here, I'll slightly adjust font size for consistency with depth.
add_subnodes(sub_id, subnode['subnodes'],
'#FFA726' if font_size > 8 else '#FFCC80', # Lighter orange/yellow for deeper levels
font_size - 1 if font_size > 7 else font_size,
'#E91E63' if edge_font_size > 7 else '#FF5252', # Reddish for deeper edges
edge_font_size - 1 if edge_font_size > 7 else edge_font_size)
# Start processing subnodes from the first level
add_subnodes(node_id, node.get('subnodes', []), '#FFA726', 10, '#E91E63', 8)
# Save to temporary file
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)}"
if __name__ == "__main__":
demo = gr.Interface(
fn=generate_concept_map,
inputs=gr.Textbox(
value=COMPLEX_SAMPLE_JSON, # ¡Ahora usa el JSON AI simétrico y complejo!
placeholder="Paste JSON following the documented format",
label="Structured JSON Input",
lines=25
),
outputs=gr.Image(
label="Generated Concept Map",
type="filepath",
show_download_button=True
),
title="Advanced Concept Map Generator (Symmetric AI)",
description="Create symmetric, multi-level concept maps for AI from properly formatted JSON."
)
demo.launch(
mcp_server=True,
share=False,
server_port=7860,
server_name="0.0.0.0"
)