ZahirJS commited on
Commit
00ba522
·
verified ·
1 Parent(s): 0412567

Create process_flow_generator.py

Browse files
Files changed (1) hide show
  1. process_flow_generator.py +153 -0
process_flow_generator.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ import os
5
+ from graph_generator_utils import add_nodes_and_edges # Reusing common utility
6
+
7
+ def generate_process_flow_diagram(json_input: str, base_color: str) -> str:
8
+ """
9
+ Generates a Process Flow Diagram (Flowchart) from JSON input.
10
+
11
+ Args:
12
+ json_input (str): A JSON string describing the process flow structure.
13
+ It must follow the Expected JSON Format Example below.
14
+ base_color (str): The hexadecimal color string (e.g., '#19191a') for the base
15
+ color of the nodes, from which a gradient will be generated.
16
+
17
+ Returns:
18
+ str: The filepath to the generated PNG image file.
19
+
20
+ Expected JSON Format Example:
21
+ {
22
+ "start_node": "Start Process",
23
+ "nodes": [
24
+ {
25
+ "id": "step1",
26
+ "label": "Gather Requirements",
27
+ "type": "process",
28
+ "relationship": "Next",
29
+ "subnodes": [
30
+ {"id": "step1_1", "label": "Define Scope", "type": "process", "relationship": "Then"}
31
+ ]
32
+ },
33
+ {
34
+ "id": "decision1",
35
+ "label": "Is Data Available?",
36
+ "type": "decision",
37
+ "relationship": "Check",
38
+ "subnodes": [
39
+ {"id": "path_yes", "label": "Process Data", "type": "process", "relationship": "Yes"},
40
+ {"id": "path_no", "label": "Collect More Data", "type": "process", "relationship": "No"}
41
+ ]
42
+ },
43
+ {
44
+ "id": "end_node",
45
+ "label": "End Process",
46
+ "type": "end",
47
+ "relationship": "Concludes"
48
+ }
49
+ ],
50
+ "connections": [
51
+ {"from": "start_node", "to": "step1"},
52
+ {"from": "step1", "to": "decision1"},
53
+ {"from": "decision1", "to": "path_yes", "label": "Yes"},
54
+ {"from": "decision1", "to": "path_no", "label": "No"},
55
+ {"from": "path_yes", "to": "end_node"},
56
+ {"from": "path_no", "to": "end_node"}
57
+ ]
58
+ }
59
+ """
60
+ try:
61
+ if not json_input.strip():
62
+ return "Error: Empty input"
63
+
64
+ data = json.loads(json_input)
65
+
66
+ # Determine specific node shapes for flowchart types
67
+ node_shapes = {
68
+ "process": "box",
69
+ "decision": "diamond",
70
+ "start": "Mrecord", # Rounded box for start/end
71
+ "end": "Mrecord", # Rounded box for start/end
72
+ "io": "parallelogram", # Input/Output
73
+ "document": "note",
74
+ "default": "box"
75
+ }
76
+
77
+ dot = graphviz.Digraph(
78
+ name='ProcessFlowDiagram',
79
+ format='png',
80
+ graph_attr={
81
+ 'rankdir': 'TB', # Top-to-Bottom flow is common for flowcharts
82
+ 'splines': 'ortho', # Straight lines with 90-degree bends
83
+ 'bgcolor': 'white', # White background
84
+ 'pad': '0.5' # Padding around the graph
85
+ },
86
+ node_attr={'style': 'filled,rounded', 'fontcolor': 'white', 'fontsize': '12'} # Default node style
87
+ )
88
+
89
+ # Add all nodes based on JSON structure
90
+ all_nodes = {}
91
+ if 'start_node' in data:
92
+ all_nodes[data['start_node']] = {"label": data['start_node'], "type": "start"}
93
+
94
+ for node_data in data.get('nodes', []):
95
+ all_nodes[node_data['id']] = node_data
96
+
97
+ # Add nodes with specific shapes and styles
98
+ for node_id, node_info in all_nodes.items():
99
+ node_type = node_info.get("type", "default")
100
+ shape = node_shapes.get(node_type, "box") # Default to box if type is unknown
101
+
102
+ # Calculate color for current node based on a simplified depth or fixed for process flow
103
+ # For simplicity in process flow, let's keep the base color for all primary nodes for now,
104
+ # or apply a subtle gradient if depth is truly defined in a meaningful way.
105
+ # Here, we'll use the base_color for all nodes, making the color selection more direct.
106
+
107
+ # Simple color lightening for sub-levels if 'subnodes' are used in a nested process
108
+ # This logic mimics graph_generator_utils but is adapted for the specific flow structure
109
+ # For a pure flowchart, often nodes are all the same color.
110
+ # If you want gradient for subprocesses, the current_depth logic needs to be more robust for different node types.
111
+
112
+ # Let's use base_color directly for all main nodes and only apply gradient for 'subnodes'
113
+ # For now, we'll pass current_depth=0 to add_nodes_and_edges when called recursively
114
+ # to ensure the main flow nodes are consistent.
115
+
116
+ dot.node(
117
+ node_id,
118
+ node_info['label'],
119
+ shape=shape,
120
+ style='filled,rounded',
121
+ fillcolor=base_color, # Use the selected base color
122
+ fontcolor='white' if base_color == '#19191a' else 'black', # Adjust for readability
123
+ fontsize='14'
124
+ )
125
+
126
+ # Add connections (edges)
127
+ for connection in data.get('connections', []):
128
+ dot.edge(
129
+ connection['from'],
130
+ connection['to'],
131
+ label=connection.get('label', ''),
132
+ color='#4a4a4a', # Dark gray for lines
133
+ fontcolor='#4a4a4a',
134
+ fontsize='10'
135
+ )
136
+
137
+ # If there's a start_node, ensure it's visually marked
138
+ if 'start_node' in data:
139
+ dot.node(data['start_node'], data['start_node'], shape=node_shapes['start'], style='filled,rounded', fillcolor='#2196F3', fontcolor='white', fontsize='14')
140
+ if 'end_node' in data and data['end_node'] in all_nodes:
141
+ dot.node(data['end_node'], all_nodes[data['end_node']]['label'], shape=node_shapes['end'], style='filled,rounded', fillcolor='#F44336', fontcolor='white', fontsize='14')
142
+
143
+
144
+ # Save to temporary file
145
+ with NamedTemporaryFile(delete=False, suffix='.png') as tmp:
146
+ dot.render(tmp.name, format='png', cleanup=True)
147
+ return tmp.name + '.png'
148
+
149
+ except json.JSONDecodeError:
150
+ return "Error: Invalid JSON format"
151
+ except Exception as e:
152
+ return f"Error: {str(e)}"
153
+