File size: 4,091 Bytes
248b059
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import graphviz
import json
from tempfile import NamedTemporaryFile
import os

def generate_network_graph(json_input: str, output_format: str) -> str:
    try:
        if not json_input.strip():
            return "Error: Empty input"
            
        data = json.loads(json_input)
        
        if 'nodes' not in data or 'connections' not in data:
            raise ValueError("Missing required fields: nodes or connections")

        dot = graphviz.Graph(
            name='NetworkGraph',
            format='png',
            engine='neato',
            graph_attr={
                'overlap': 'false',
                'splines': 'true',
                'bgcolor': 'white',
                'pad': '0.5',
                'layout': 'neato'
            },
            node_attr={
                'fixedsize': 'false'
            }
        )
        
        base_color = '#19191a'
        lightening_factor = 0.12
        
        nodes = data.get('nodes', [])
        connections = data.get('connections', [])
        
        for i, node in enumerate(nodes):
            node_id = node.get('id')
            label = node.get('label')
            node_type = node.get('type', 'default')
            
            if not all([node_id, label]):
                raise ValueError(f"Invalid node: {node}")
            
            current_depth = node.get('depth', i % 5)
            
            if not isinstance(base_color, str) or not base_color.startswith('#') or len(base_color) != 7:
                base_color_safe = '#19191a'
            else:
                base_color_safe = base_color
                
            base_r = int(base_color_safe[1:3], 16)
            base_g = int(base_color_safe[3:5], 16)
            base_b = int(base_color_safe[5:7], 16)
            
            current_r = base_r + int((255 - base_r) * current_depth * lightening_factor)
            current_g = base_g + int((255 - base_g) * current_depth * lightening_factor)
            current_b = base_b + int((255 - base_b) * current_depth * lightening_factor)
            
            current_r = min(255, current_r)
            current_g = min(255, current_g)
            current_b = min(255, current_b)
            
            node_color = f'#{current_r:02x}{current_g:02x}{current_b:02x}'
            font_color = 'white' if current_depth * lightening_factor < 0.6 else 'black'
            font_size = max(9, 14 - (current_depth * 1))
            
            if node_type == 'server':
                shape = 'box'
            elif node_type == 'database':
                shape = 'cylinder'
            elif node_type == 'user':
                shape = 'ellipse'
            elif node_type == 'service':
                shape = 'hexagon'
            else:
                shape = 'circle'
            
            dot.node(
                node_id,
                label,
                shape=shape,
                style='filled',
                fillcolor=node_color,
                fontcolor=font_color,
                fontsize=str(font_size)
            )
        
        for connection in connections:
            from_node = connection.get('from')
            to_node = connection.get('to')
            label = connection.get('label', '')
            weight = connection.get('weight', 1)
            
            if not all([from_node, to_node]):
                raise ValueError(f"Invalid connection: {connection}")
            
            penwidth = str(max(1, min(5, weight)))
            
            dot.edge(
                from_node,
                to_node,
                label=label,
                color='#4a4a4a',
                fontcolor='#4a4a4a',
                fontsize='10',
                penwidth=penwidth
            )

        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)}"