| import gradio as gr |
| import json |
| from graphviz import Digraph |
| import base64 |
|
|
| def generate_concept_map(json_input: str) -> str: |
| """ |
| Generate concept map from JSON and return as base64 image |
| |
| Args: |
| json_input (str): JSON describing the concept map structure. |
| Required format: |
| { |
| "central_node": "Main concept", |
| "nodes": [ |
| { |
| "id": "unique_identifier", |
| "label": "Node label", |
| "relationship": "Relationship to parent", |
| "subnodes": [...] # Optional |
| } |
| ] |
| } |
| |
| Returns: |
| str: Base64 data URL of the generated concept map |
| """ |
| 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 = Digraph( |
| name='ConceptMap', |
| format='png', |
| graph_attr={ |
| 'rankdir': 'TB', |
| 'splines': 'ortho', |
| 'bgcolor': 'transparent' |
| } |
| ) |
| |
| |
| dot.node( |
| 'central', |
| data['central_node'], |
| shape='ellipse', |
| style='filled', |
| fillcolor='#2196F3', |
| fontcolor='white', |
| fontsize='14' |
| ) |
| |
| |
| for node in data['nodes']: |
| node_id = node.get('id') |
| label = node.get('label') |
| relationship = node.get('relationship') |
| |
| |
| if not all([node_id, label, relationship]): |
| raise ValueError(f"Invalid node: {node}") |
| |
| |
| dot.node( |
| node_id, |
| label, |
| shape='box', |
| style='filled', |
| fillcolor='#4CAF50', |
| fontcolor='white', |
| fontsize='12' |
| ) |
| |
| |
| dot.edge( |
| 'central', |
| node_id, |
| label=relationship, |
| color='#9C27B0', |
| fontsize='10' |
| ) |
| |
| |
| for subnode in node.get('subnodes', []): |
| 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='diamond', |
| style='filled', |
| fillcolor='#FF5722', |
| fontcolor='white', |
| fontsize='10' |
| ) |
| |
| dot.edge( |
| node_id, |
| sub_id, |
| label=sub_rel, |
| color='#E91E63', |
| fontsize='8' |
| ) |
|
|
| |
| img_data = dot.pipe(format='png') |
| img_base64 = base64.b64encode(img_data).decode() |
| return f"data:image/png;base64,{img_base64}" |
|
|
| except json.JSONDecodeError: |
| return "Error: Invalid JSON format" |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
| if __name__ == "__main__": |
| |
| sample_json = """ |
| { |
| "central_node": "Artificial Intelligence (AI)", |
| "nodes": [ |
| { |
| "id": "ml", |
| "label": "Machine Learning", |
| "relationship": "core_component", |
| "subnodes": [ |
| {"id": "sl", "label": "Supervised Learning", "relationship": "type_of"}, |
| {"id": "ul", "label": "Unsupervised Learning", "relationship": "type_of"} |
| ] |
| }, |
| { |
| "id": "nlp", |
| "label": "Natural Language Processing", |
| "relationship": "application_area", |
| "subnodes": [ |
| {"id": "sa", "label": "Sentiment Analysis", "relationship": "technique"}, |
| {"id": "tr", "label": "Translation", "relationship": "technique"} |
| ] |
| } |
| ] |
| } |
| """ |
| |
| demo = gr.Interface( |
| fn=generate_concept_map, |
| inputs=gr.Textbox( |
| value=sample_json, |
| placeholder="Paste structured JSON here...", |
| label="JSON Input", |
| lines=15 |
| ), |
| outputs=gr.Textbox( |
| label="Image URL", |
| placeholder="Base64 image URL will appear here" |
| ), |
| title="Concept Map Generator", |
| description="Create concept maps from JSON for web display" |
| ) |
| |
| demo.launch( |
| mcp_server=True, |
| share=False, |
| server_port=7860, |
| server_name="0.0.0.0" |
| ) |