Anupam007 commited on
Commit
37e5a6d
·
verified ·
1 Parent(s): 44471e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -69
app.py CHANGED
@@ -3,47 +3,42 @@ import requests
3
  import spacy
4
  import re
5
  import time
6
- from IPython.display import display, HTML
 
 
 
 
 
 
 
 
 
 
7
 
8
- # Load the English NLP model
9
- nlp = spacy.load("en_core_web_sm")
10
 
11
- # Function to parse the input and generate a Mermaid.js diagram
12
  def text_to_diagram(text_description):
13
- """
14
- Convert any process description into a Mermaid.js flowchart by parsing steps, conditions, loops, and flows.
15
- """
16
- # Process the text with spaCy
17
  doc = nlp(text_description.lower())
18
-
19
- # Initialize variables to store diagram components
20
- steps = [] # List of sequential steps
21
- conditions = [] # List of (condition, true_action, false_action)
22
- loops = [] # List of (action, condition)
23
- complex_flow = [] # List of steps in the complex flow
24
-
25
- # Split the text into sentences for easier parsing
26
  sentences = [sent.text.strip() for sent in doc.sents]
27
 
28
- # Parse each sentence to identify components
29
  for sentence in sentences:
30
- # Clean up the sentence
31
  sentence = sentence.replace("2, conditional branch", "").replace("3 loop start", "").replace("4 complex flow", "").strip()
32
 
33
- # Detect sequential steps (e.g., "Developer writes code, then tests code")
34
  if "then" in sentence:
35
  tokens = sentence.split("then")
36
  for token in tokens:
37
  token = token.strip()
38
  if token:
39
- # Extract the full action (e.g., "developer writes code" instead of just "code")
40
  step = token.strip().replace(",", "").replace(".", "")
41
  if step and step not in ["conditional branch", "loop start", "complex flow"]:
42
  if "flowchart that shows" in step:
43
  continue
44
  steps.append(step)
45
 
46
- # Detect conditional branches (e.g., "If tests pass, proceed to deploy; otherwise, debug code")
47
  cond_match = re.search(r"if ([a-z\s]+), (?:go to|proceed to) ([a-z\s]+); otherwise, (?:go to|proceed to) ([a-z\s]+)", sentence)
48
  if not cond_match:
49
  cond_match = re.search(r"if ([a-z\s]+), ([a-z\s]+); otherwise, ([a-z\s]+)", sentence)
@@ -51,7 +46,6 @@ def text_to_diagram(text_description):
51
  condition, true_action, false_action = cond_match.groups()
52
  conditions.append((condition.strip(), true_action.strip(), false_action.strip()))
53
 
54
- # Detect loops (e.g., "Loop: Perform action X, repeat X until Y is true")
55
  loop_match = re.search(r"(?:do|perform) (?:action )?([a-z]), repeat ([a-z]) until ([a-z]) is true", sentence)
56
  if not loop_match:
57
  loop_match = re.search(r"loop: ([a-z\s]+), repeat ([a-z\s]+) until ([a-z\s]+)", sentence)
@@ -59,7 +53,6 @@ def text_to_diagram(text_description):
59
  action, repeat_action, condition = loop_match.groups()
60
  loops.append((action.strip(), condition.strip()))
61
 
62
- # Detect complex flow (e.g., "A flowchart that shows a customer replacing an order, then fulfilled, then ship, then delivered")
63
  if "flowchart that shows" in sentence:
64
  flow_part = sentence.split("flowchart that shows")[1]
65
  flow_steps = flow_part.split("then")
@@ -67,7 +60,7 @@ def text_to_diagram(text_description):
67
  step = step.replace("and finally", "").replace(".", "").replace(",", "").strip()
68
  if step:
69
  complex_flow.append(step)
70
- elif "then" in sentence and not steps: # If no "flowchart that shows", treat remaining "then" as complex flow
71
  tokens = sentence.split("then")
72
  for token in tokens:
73
  token = token.strip()
@@ -76,39 +69,33 @@ def text_to_diagram(text_description):
76
  if step:
77
  complex_flow.append(step)
78
 
79
- # Combine steps and complex flow if necessary
80
  if not complex_flow:
81
  complex_flow = steps[1:] if steps else []
82
  steps = steps[:1] if steps else []
83
 
84
- # Build the Mermaid.js diagram
85
  diagram_code = "graph TD\n"
86
  node_counter = 0
87
- node_map = {} # Map labels to node IDs (e.g., "developer writes code" -> "A")
88
 
89
- # Helper function to get or create a node ID
90
  def get_node_id(label):
91
  nonlocal node_counter
92
  if label not in node_map:
93
- node_map[label] = chr(65 + node_counter) # A, B, C, ...
94
  node_counter += 1
95
  return node_map[label]
96
 
97
- # Add initial steps
98
  if steps:
99
  for i in range(len(steps)):
100
  step = steps[i]
101
  node_id = get_node_id(step)
102
  diagram_code += f"{node_id}[{step.capitalize()}]\n"
103
  if i > 0:
104
- prev_node_id = get_node_id(steps[i-1])
105
  diagram_code += f"{prev_node_id} --> {node_id}\n"
106
  else:
107
- # Default start node if no initial steps
108
  start_node = get_node_id("start")
109
  diagram_code += f"{start_node}[Start]\n"
110
 
111
- # Add conditional branches
112
  last_node = steps[-1] if steps else "start"
113
  last_node_id = get_node_id(last_node)
114
  for condition, true_action, false_action in conditions:
@@ -118,11 +105,10 @@ def text_to_diagram(text_description):
118
  diagram_code += f"{last_node_id} --> {cond_node}{{{condition.capitalize()}?}}\n"
119
  diagram_code += f"{cond_node} -->|Yes| {true_node}[{true_action.capitalize()}]\n"
120
  diagram_code += f"{cond_node} -->|No| {false_node}[{false_action.capitalize()}]\n"
121
- last_node = true_action # Continue from the "true" path
122
  last_node_id = true_node
123
- false_node_id = false_node # Store the "false" path for later
124
 
125
- # Add loops
126
  for action, condition in loops:
127
  loop_node = get_node_id(f"loop_{action}")
128
  cond_node = get_node_id(f"cond_{condition}")
@@ -133,7 +119,6 @@ def text_to_diagram(text_description):
133
  diagram_code += f"{cond_node} -->|Yes| {finish_node}[Finish Loop]\n"
134
  last_node_id = finish_node
135
 
136
- # Add complex flow
137
  for i in range(len(complex_flow)):
138
  step = complex_flow[i]
139
  node_id = get_node_id(f"flow_{i}_{step}")
@@ -141,58 +126,43 @@ def text_to_diagram(text_description):
141
  if i == 0:
142
  diagram_code += f"{last_node_id} --> {node_id}\n"
143
  else:
144
- prev_node_id = get_node_id(f"flow_{i-1}_{complex_flow[i-1]}")
145
  diagram_code += f"{prev_node_id} --> {node_id}\n"
146
  last_node_id = node_id
147
 
148
- # Add end node
149
  end_node = get_node_id("end")
150
  diagram_code += f"{end_node}[End Process]\n"
151
  diagram_code += f"{last_node_id} --> {end_node}\n"
152
 
153
- # Connect the "false" path of conditions to the end
154
  if conditions:
155
  diagram_code += f"{false_node_id} --> {end_node}\n"
156
 
157
- # Render diagram to image using Mermaid.ink API
158
  try:
159
  img_url = render_mermaid_to_url(diagram_code)
160
  response = requests.get(img_url, timeout=10)
161
- if response.status_code == 200:
162
- image_data = response.content
163
- temp_img_path = f"temp_diagram_{int(time.time())}.png"
164
- with open(temp_img_path, "wb") as f:
165
- f.write(image_data)
166
- return diagram_code, temp_img_path
167
- else:
168
- return diagram_code, None
 
169
  except Exception as e:
170
  print(f"Error rendering diagram: {e}")
171
  return diagram_code, None
172
 
 
173
  def render_mermaid_to_url(mermaid_code):
174
  """Render Mermaid code to an image URL using Mermaid.ink."""
175
  try:
176
- import base64
177
  encoded_code = base64.urlsafe_b64encode(mermaid_code.encode()).decode()
178
  return f"https://mermaid.ink/img/{encoded_code}"
179
  except Exception as e:
180
  print(f"Error encoding Mermaid code: {e}")
181
  return None
182
 
183
- # Direct rendering for Jupyter/Colab (optional)
184
- def render_mermaid_in_notebook(mermaid_code):
185
- """Render Mermaid diagram in a notebook environment."""
186
- html = f"""
187
- <div class="mermaid">
188
- {mermaid_code}
189
- </div>
190
- <script src="https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.min.js"></script>
191
- <script>mermaid.initialize({{startOnLoad:true}});</script>
192
- """
193
- return HTML(html)
194
-
195
- # Gradio Interface
196
  def gradio_interface(text_input):
197
  """Process user input and return diagram output for Gradio."""
198
  try:
@@ -204,16 +174,15 @@ def gradio_interface(text_input):
204
  print(f"Error in Gradio interface: {e}")
205
  return f"Error: {str(e)}", None
206
 
207
- # Create Gradio interface
208
  iface = gr.Interface(
209
  fn=gradio_interface,
210
  inputs=gr.Textbox(lines=5, placeholder="Describe your process (e.g., 'User logs in, then enters details...')"),
211
  outputs=[
212
  gr.Textbox(label="Generated Mermaid Code"),
213
- gr.Image(label="Diagram Visualization", type="filepath")
214
  ],
215
  title="Text-to-Diagram Converter (Napkin AI Style)",
216
- description="Convert any process description into a structured flowchart like Napkin AI.",
217
  examples=[
218
  ["Developer writes code, then tests code. If tests pass, proceed to deploy; otherwise, debug code. Then release software to production."],
219
  ["Customer browses products, then adds to cart, then proceeds to checkout. If payment is verified, confirm order; otherwise, request payment again. Loop: Validate payment, repeat until successful. Then ship order and notify customer."],
@@ -221,11 +190,10 @@ iface = gr.Interface(
221
  ["Student registers for exam, then studies material. If prepared, proceed to take exam; otherwise, study more. If exam is passed, receive certificate; otherwise, retake exam. Then celebrate success."],
222
  ["Customer places order, then kitchen prepares food. Loop: Check food quality, repeat until perfect. Then serve food and collect payment."],
223
  ["Traveler searches for flights, then selects flight, then books ticket. If booking is confirmed, proceed to payment; otherwise, search again. If payment is successful, receive itinerary; otherwise, cancel booking. Then plan trip."],
224
- ["User signs up, then verifies email, then logs in."]
225
  ],
226
- allow_flagging="never"
227
  )
228
 
229
- # Launch the interface
230
  if __name__ == "__main__":
231
  iface.launch(share=True)
 
3
  import spacy
4
  import re
5
  import time
6
+ import os # Import the 'os' module
7
+ import base64 # Import base64
8
+
9
+ # Try to load the spaCy model, download if it's not available
10
+ try:
11
+ nlp = spacy.load("en_core_web_sm")
12
+ except OSError:
13
+ print("Downloading en_core_web_sm model...")
14
+ import spacy.cli
15
+ spacy.cli.download("en_core_web_sm")
16
+ nlp = spacy.load("en_core_web_sm")
17
 
 
 
18
 
 
19
  def text_to_diagram(text_description):
20
+ """Convert process description into Mermaid.js flowchart."""
 
 
 
21
  doc = nlp(text_description.lower())
22
+ steps = []
23
+ conditions = []
24
+ loops = []
25
+ complex_flow = []
 
 
 
 
26
  sentences = [sent.text.strip() for sent in doc.sents]
27
 
 
28
  for sentence in sentences:
 
29
  sentence = sentence.replace("2, conditional branch", "").replace("3 loop start", "").replace("4 complex flow", "").strip()
30
 
 
31
  if "then" in sentence:
32
  tokens = sentence.split("then")
33
  for token in tokens:
34
  token = token.strip()
35
  if token:
 
36
  step = token.strip().replace(",", "").replace(".", "")
37
  if step and step not in ["conditional branch", "loop start", "complex flow"]:
38
  if "flowchart that shows" in step:
39
  continue
40
  steps.append(step)
41
 
 
42
  cond_match = re.search(r"if ([a-z\s]+), (?:go to|proceed to) ([a-z\s]+); otherwise, (?:go to|proceed to) ([a-z\s]+)", sentence)
43
  if not cond_match:
44
  cond_match = re.search(r"if ([a-z\s]+), ([a-z\s]+); otherwise, ([a-z\s]+)", sentence)
 
46
  condition, true_action, false_action = cond_match.groups()
47
  conditions.append((condition.strip(), true_action.strip(), false_action.strip()))
48
 
 
49
  loop_match = re.search(r"(?:do|perform) (?:action )?([a-z]), repeat ([a-z]) until ([a-z]) is true", sentence)
50
  if not loop_match:
51
  loop_match = re.search(r"loop: ([a-z\s]+), repeat ([a-z\s]+) until ([a-z\s]+)", sentence)
 
53
  action, repeat_action, condition = loop_match.groups()
54
  loops.append((action.strip(), condition.strip()))
55
 
 
56
  if "flowchart that shows" in sentence:
57
  flow_part = sentence.split("flowchart that shows")[1]
58
  flow_steps = flow_part.split("then")
 
60
  step = step.replace("and finally", "").replace(".", "").replace(",", "").strip()
61
  if step:
62
  complex_flow.append(step)
63
+ elif "then" in sentence and not steps:
64
  tokens = sentence.split("then")
65
  for token in tokens:
66
  token = token.strip()
 
69
  if step:
70
  complex_flow.append(step)
71
 
 
72
  if not complex_flow:
73
  complex_flow = steps[1:] if steps else []
74
  steps = steps[:1] if steps else []
75
 
 
76
  diagram_code = "graph TD\n"
77
  node_counter = 0
78
+ node_map = {}
79
 
 
80
  def get_node_id(label):
81
  nonlocal node_counter
82
  if label not in node_map:
83
+ node_map[label] = chr(65 + node_counter)
84
  node_counter += 1
85
  return node_map[label]
86
 
 
87
  if steps:
88
  for i in range(len(steps)):
89
  step = steps[i]
90
  node_id = get_node_id(step)
91
  diagram_code += f"{node_id}[{step.capitalize()}]\n"
92
  if i > 0:
93
+ prev_node_id = get_node_id(steps[i - 1])
94
  diagram_code += f"{prev_node_id} --> {node_id}\n"
95
  else:
 
96
  start_node = get_node_id("start")
97
  diagram_code += f"{start_node}[Start]\n"
98
 
 
99
  last_node = steps[-1] if steps else "start"
100
  last_node_id = get_node_id(last_node)
101
  for condition, true_action, false_action in conditions:
 
105
  diagram_code += f"{last_node_id} --> {cond_node}{{{condition.capitalize()}?}}\n"
106
  diagram_code += f"{cond_node} -->|Yes| {true_node}[{true_action.capitalize()}]\n"
107
  diagram_code += f"{cond_node} -->|No| {false_node}[{false_action.capitalize()}]\n"
108
+ last_node = true_action
109
  last_node_id = true_node
110
+ false_node_id = false_node
111
 
 
112
  for action, condition in loops:
113
  loop_node = get_node_id(f"loop_{action}")
114
  cond_node = get_node_id(f"cond_{condition}")
 
119
  diagram_code += f"{cond_node} -->|Yes| {finish_node}[Finish Loop]\n"
120
  last_node_id = finish_node
121
 
 
122
  for i in range(len(complex_flow)):
123
  step = complex_flow[i]
124
  node_id = get_node_id(f"flow_{i}_{step}")
 
126
  if i == 0:
127
  diagram_code += f"{last_node_id} --> {node_id}\n"
128
  else:
129
+ prev_node_id = get_node_id(f"flow_{i - 1}_{complex_flow[i - 1]}")
130
  diagram_code += f"{prev_node_id} --> {node_id}\n"
131
  last_node_id = node_id
132
 
 
133
  end_node = get_node_id("end")
134
  diagram_code += f"{end_node}[End Process]\n"
135
  diagram_code += f"{last_node_id} --> {end_node}\n"
136
 
 
137
  if conditions:
138
  diagram_code += f"{false_node_id} --> {end_node}\n"
139
 
 
140
  try:
141
  img_url = render_mermaid_to_url(diagram_code)
142
  response = requests.get(img_url, timeout=10)
143
+ response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
144
+ image_data = response.content
145
+ temp_img_path = f"temp_diagram_{int(time.time())}.png"
146
+ with open(temp_img_path, "wb") as f:
147
+ f.write(image_data)
148
+ return diagram_code, temp_img_path
149
+ except requests.exceptions.RequestException as e:
150
+ print(f"Error rendering diagram (network): {e}")
151
+ return diagram_code, None
152
  except Exception as e:
153
  print(f"Error rendering diagram: {e}")
154
  return diagram_code, None
155
 
156
+
157
  def render_mermaid_to_url(mermaid_code):
158
  """Render Mermaid code to an image URL using Mermaid.ink."""
159
  try:
 
160
  encoded_code = base64.urlsafe_b64encode(mermaid_code.encode()).decode()
161
  return f"https://mermaid.ink/img/{encoded_code}"
162
  except Exception as e:
163
  print(f"Error encoding Mermaid code: {e}")
164
  return None
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  def gradio_interface(text_input):
167
  """Process user input and return diagram output for Gradio."""
168
  try:
 
174
  print(f"Error in Gradio interface: {e}")
175
  return f"Error: {str(e)}", None
176
 
 
177
  iface = gr.Interface(
178
  fn=gradio_interface,
179
  inputs=gr.Textbox(lines=5, placeholder="Describe your process (e.g., 'User logs in, then enters details...')"),
180
  outputs=[
181
  gr.Textbox(label="Generated Mermaid Code"),
182
+ gr.Image(label="Diagram Visualization", type="filepath"),
183
  ],
184
  title="Text-to-Diagram Converter (Napkin AI Style)",
185
+ description="Convert process descriptions into structured flowcharts like Napkin AI.",
186
  examples=[
187
  ["Developer writes code, then tests code. If tests pass, proceed to deploy; otherwise, debug code. Then release software to production."],
188
  ["Customer browses products, then adds to cart, then proceeds to checkout. If payment is verified, confirm order; otherwise, request payment again. Loop: Validate payment, repeat until successful. Then ship order and notify customer."],
 
190
  ["Student registers for exam, then studies material. If prepared, proceed to take exam; otherwise, study more. If exam is passed, receive certificate; otherwise, retake exam. Then celebrate success."],
191
  ["Customer places order, then kitchen prepares food. Loop: Check food quality, repeat until perfect. Then serve food and collect payment."],
192
  ["Traveler searches for flights, then selects flight, then books ticket. If booking is confirmed, proceed to payment; otherwise, search again. If payment is successful, receive itinerary; otherwise, cancel booking. Then plan trip."],
193
+ ["User signs up, then verifies email, then logs in."],
194
  ],
195
+ allow_flagging="never",
196
  )
197
 
 
198
  if __name__ == "__main__":
199
  iface.launch(share=True)