dina1 commited on
Commit
ad01bea
·
verified ·
1 Parent(s): d94a6bf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -114
app.py CHANGED
@@ -1,6 +1,6 @@
1
  # =========================================================
2
- # Intelligent PowerApps Mockup Generator
3
- # From Business Requirement PDF → Multi-Screen PowerApps-Style Mockup Images
4
  # =========================================================
5
 
6
  import google.generativeai as genai
@@ -11,6 +11,7 @@ import uuid
11
  import os
12
  import re
13
  import time
 
14
 
15
  # -----------------------
16
  # Configure Gemini API
@@ -63,14 +64,11 @@ body {
63
  width: 16px;
64
  height: 16px;
65
  margin-right: 8px;
 
66
  }
67
  .sidebar-item span { color: #ccc; }
68
  .sidebar-item:hover { background: #3a3a3a; }
69
- .sidebar-item.active {
70
- background: #0078d4;
71
- color: #fff;
72
- }
73
- .sidebar-item.active span { color: #fff; }
74
  .sidebar-section {
75
  margin-top: 14px;
76
  padding: 8px 18px;
@@ -88,37 +86,13 @@ body {
88
  padding: 0 20px;
89
  font-weight: bold;
90
  }
91
- .main {
92
- margin-left: 240px;
93
- padding: 20px;
94
- }
95
- .screen {
96
- display: none;
97
- }
98
- .screen.active {
99
- display: block;
100
- }
101
  </style>
102
- <script>
103
- function showScreen(name) {
104
- document.querySelectorAll('.screen').forEach(el => el.classList.remove('active'));
105
- document.querySelectorAll('.sidebar-item').forEach(el => el.classList.remove('active'));
106
-
107
- const target = document.querySelector(`[data-screen='${name}']`);
108
- if (target) target.classList.add('active');
109
-
110
- const activeItem = Array.from(document.querySelectorAll('.sidebar-item'))
111
- .find(el => el.textContent.trim() === name);
112
- if (activeItem) activeItem.classList.add('active');
113
- }
114
- </script>
115
  </head>
116
  <body>
117
  {user_sidebar}
118
  <div class="topbar">{app_title}</div>
119
- <div class="main">
120
- {user_content}
121
- </div>
122
  </body>
123
  </html>
124
  """
@@ -134,18 +108,12 @@ SVG_FLUENT = {
134
  }
135
 
136
  # -----------------------
137
- # Sidebar Generator
138
- # -----------------------
139
- # -----------------------
140
- # -----------------------
141
- # Sidebar Generator (Interactive version)
142
  # -----------------------
143
- def generate_sidebar(app_title, screens, active_label="Dashboard"):
144
  """
145
- Builds sidebar with clickable screen items.
146
- The current screen (active_label) is highlighted.
147
  """
148
-
149
  SCREEN_ICONS = [
150
  """<svg viewBox="0 0 24 24"><path fill="#c5c5c5" d="M3 13h2v-2H3v2zm4 0h14v-2H7v2zm-4 5h2v-2H3v2zm4 0h14v-2H7v2zM3 8h2V6H3v2zm4 0h14V6H7v2z"/></svg>""",
151
  """<svg viewBox="0 0 24 24"><path fill="#c5c5c5" d="M19 3H5a2 2 0 0 0-2 2v14l4-4h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z"/></svg>""",
@@ -158,29 +126,28 @@ def generate_sidebar(app_title, screens, active_label="Dashboard"):
158
  sidebar_html = "<div class='sidebar'>"
159
  sidebar_html += f"<div class='sidebar-header'>{SVG_FLUENT['hamburger']} {app_title}</div>"
160
 
161
- # Default static options
162
  defaults = [("home", "Home"), ("recent", "Recent"), ("pinned", "Pinned")]
163
  for key, label in defaults:
164
  sidebar_html += f"<div class='sidebar-item'>{SVG_FLUENT[key]}<span>{label}</span></div>"
165
 
166
- # Dynamic “My Work” section with clickable screens
167
- sidebar_html += "<div class='sidebar-section'>My Work</div>"
168
- for i, screen in enumerate(screens):
169
- screen_name = screen.get("screen_name", f"Screen {i+1}")
170
- active_class = "active" if screen_name == active_label else ""
171
- icon_svg = SCREEN_ICONS[i % len(SCREEN_ICONS)]
172
- if active_class:
173
- icon_svg = icon_svg.replace("#c5c5c5", "#fff")
174
- sidebar_html += (
175
- f"<div class='sidebar-item {active_class}' onclick=\"showScreen('{screen_name}')\">"
176
- f"{icon_svg}<span>{screen_name}</span></div>"
177
- )
178
 
179
  sidebar_html += "</div>"
180
  return sidebar_html
181
 
182
  # -----------------------
183
- # Analyze Business PDF → Generate Multi-Screen HTML
184
  # -----------------------
185
  def analyze_business_pdf(pdf_file):
186
  text = ""
@@ -199,58 +166,57 @@ def analyze_business_pdf(pdf_file):
199
  except Exception:
200
  app_title = "PowerApp Solution"
201
 
202
- # Step 2: Ask Gemini to identify screens and their HTML layouts
203
  try:
204
  response = model.generate_content(
205
- "You are a PowerApps UI designer. Analyze the following business requirements and infer multiple PowerApps screens if applicable. "
206
- "Return structured JSON like this:\n"
207
- "[{\"screen_name\": \"Dashboard\", \"html\": \"<div>...</div>\"}, {\"screen_name\": \"Reports\", \"html\": \"<div>...</div>\"}]\n\n"
208
- "Each HTML should represent a PowerApps-style UI (forms, tables, charts, buttons, labels). "
209
- "Do not include explanations. Return valid JSON only."
 
 
 
 
 
 
210
  + "\n\n" + text
211
  )
212
  cleaned = re.sub(r"```(?:json)?|```", "", response.text.strip())
213
- screens = []
214
- try:
215
- screens = eval(cleaned) if cleaned.strip().startswith("[") else []
216
- except Exception:
217
- screens = [{"screen_name": "Dashboard", "html": "<h2>Fallback Layout</h2><p>Unable to parse screen data.</p>"}]
218
  except Exception as e:
219
- print("⚠️ Gemini failed to create screens:", e)
220
- screens = [{"screen_name": "Dashboard", "html": "<h2>Error</h2><p>Gemini failed to process this PDF.</p>"}]
 
 
 
221
 
222
- return app_title, screens
223
 
224
  # -----------------------
225
  # Generate HTML + Image per Screen
226
  # -----------------------
227
- def generate_mockups(app_title, screens):
228
- """
229
- Generates one interactive HTML mockup (clickable navigation).
230
- """
231
- sidebar_html = generate_sidebar(app_title, screens, active_label=screens[0].get("screen_name"))
232
- screens_html = ""
233
-
234
- for i, screen in enumerate(screens):
235
- name = screen.get("screen_name", f"Screen {i+1}")
236
- html = screen.get("html", "<p>Empty</p>")
237
- active_class = "active" if i == 0 else ""
238
- screens_html += f"<div class='screen {active_class}' data-screen='{name}'>{html}</div>"
239
 
240
- full_html = BASE_TEMPLATE.replace("{user_sidebar}", sidebar_html)\
241
- .replace("{user_content}", screens_html)\
242
- .replace("{app_title}", app_title)
 
243
 
244
- uid = str(uuid.uuid4())[:8]
245
- html_path = f"mockup_interactive_{uid}.html"
246
- with open(html_path, "w", encoding="utf-8") as f:
247
- f.write(full_html)
 
 
 
248
 
249
- # Generate one image preview (first screen)
250
- img_path = f"mockup_preview_{uid}.png"
251
- hti.screenshot(html_file=html_path, save_as=img_path)
252
-
253
- return [img_path]
254
 
255
  # -----------------------
256
  # Full Pipeline with Retry Logic
@@ -258,8 +224,8 @@ def generate_mockups(app_title, screens):
258
  def generate_from_pdf(pdf_file):
259
  for attempt in range(3):
260
  try:
261
- app_title, screens = analyze_business_pdf(pdf_file)
262
- image_paths = generate_mockups(app_title, screens)
263
  return image_paths
264
  except Exception as e:
265
  if "429" in str(e) or "quota" in str(e).lower():
@@ -269,28 +235,15 @@ def generate_from_pdf(pdf_file):
269
  raise e
270
  raise Exception("❌ Failed after 3 retries due to Gemini API quota limits.")
271
 
272
- # -----------------------
273
- # Gradio UI
274
- # -----------------------
275
  # -----------------------
276
  # Gradio UI
277
  # -----------------------
278
  with gr.Blocks() as demo:
279
- gr.Markdown("## 🧩 Intelligent PowerApps Mockup Generator (Interactive Prototype Mode)")
 
280
  pdf_input = gr.File(label="📄 Upload Business Requirement PDF", file_types=[".pdf"])
281
- generate_btn = gr.Button("🚀 Generate Interactive App")
282
- gallery_output = gr.Gallery(label="Generated Preview", show_label=True, columns=2)
283
- html_link = gr.HTML()
284
-
285
- def generate_and_return_link(pdf_file):
286
- images = generate_from_pdf(pdf_file)
287
- html_files = [f for f in os.listdir('.') if f.startswith('mockup_interactive_') and f.endswith('.html')]
288
- if html_files:
289
- last_html = sorted(html_files, key=os.path.getmtime)[-1]
290
- html_url = f"<a href='file://{os.path.abspath(last_html)}' target='_blank' style='font-size:16px;color:#0078d4;text-decoration:none;'>🔗 Open Interactive Prototype</a>"
291
- return images, html_url
292
- return images, "<p style='color:red;'>No HTML file found.</p>"
293
-
294
- generate_btn.click(fn=generate_and_return_link, inputs=pdf_input, outputs=[gallery_output, html_link])
295
 
296
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  # =========================================================
2
+ # Intelligent PowerApps Mockup Generator (with Section Grouping)
3
+ # From Business Requirement PDF → Multi-Screen, Grouped PowerApps-Style Mockups
4
  # =========================================================
5
 
6
  import google.generativeai as genai
 
11
  import os
12
  import re
13
  import time
14
+ import json
15
 
16
  # -----------------------
17
  # Configure Gemini API
 
64
  width: 16px;
65
  height: 16px;
66
  margin-right: 8px;
67
+ fill: #c5c5c5;
68
  }
69
  .sidebar-item span { color: #ccc; }
70
  .sidebar-item:hover { background: #3a3a3a; }
71
+ .sidebar-item.active { background: #0078d4; color: #fff; }
 
 
 
 
72
  .sidebar-section {
73
  margin-top: 14px;
74
  padding: 8px 18px;
 
86
  padding: 0 20px;
87
  font-weight: bold;
88
  }
89
+ .main { margin-left: 240px; padding: 20px; }
 
 
 
 
 
 
 
 
 
90
  </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </head>
92
  <body>
93
  {user_sidebar}
94
  <div class="topbar">{app_title}</div>
95
+ <div class="main">{user_content}</div>
 
 
96
  </body>
97
  </html>
98
  """
 
108
  }
109
 
110
  # -----------------------
111
+ # Sidebar Generator (Grouped + Icons + Active Highlight)
 
 
 
 
112
  # -----------------------
113
+ def generate_sidebar(app_title, grouped_screens, active_label="Dashboard"):
114
  """
115
+ Builds a dynamic sidebar with section groupings, icons, and active highlight.
 
116
  """
 
117
  SCREEN_ICONS = [
118
  """<svg viewBox="0 0 24 24"><path fill="#c5c5c5" d="M3 13h2v-2H3v2zm4 0h14v-2H7v2zm-4 5h2v-2H3v2zm4 0h14v-2H7v2zM3 8h2V6H3v2zm4 0h14V6H7v2z"/></svg>""",
119
  """<svg viewBox="0 0 24 24"><path fill="#c5c5c5" d="M19 3H5a2 2 0 0 0-2 2v14l4-4h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z"/></svg>""",
 
126
  sidebar_html = "<div class='sidebar'>"
127
  sidebar_html += f"<div class='sidebar-header'>{SVG_FLUENT['hamburger']} {app_title}</div>"
128
 
129
+ # Default static items
130
  defaults = [("home", "Home"), ("recent", "Recent"), ("pinned", "Pinned")]
131
  for key, label in defaults:
132
  sidebar_html += f"<div class='sidebar-item'>{SVG_FLUENT[key]}<span>{label}</span></div>"
133
 
134
+ # Grouped section rendering
135
+ for group in grouped_screens:
136
+ section = group.get("section", "My Work")
137
+ sidebar_html += f"<div class='sidebar-section'>{section}</div>"
138
+ for i, screen in enumerate(group.get("screens", [])):
139
+ screen_name = screen.get("screen_name", f"Screen {i+1}")
140
+ active_class = "active" if screen_name == active_label else ""
141
+ icon_svg = SCREEN_ICONS[i % len(SCREEN_ICONS)]
142
+ if active_class:
143
+ icon_svg = icon_svg.replace("#c5c5c5", "#fff")
144
+ sidebar_html += f"<div class='sidebar-item {active_class}'>{icon_svg}<span>{screen_name}</span></div>"
 
145
 
146
  sidebar_html += "</div>"
147
  return sidebar_html
148
 
149
  # -----------------------
150
+ # Analyze Business PDF → Generate Grouped Screens
151
  # -----------------------
152
  def analyze_business_pdf(pdf_file):
153
  text = ""
 
166
  except Exception:
167
  app_title = "PowerApp Solution"
168
 
169
+ # Step 2: Detect grouped screen structure
170
  try:
171
  response = model.generate_content(
172
+ "You are a PowerApps UI/UX architect. Analyze the following business requirements and design a multi-screen PowerApp. "
173
+ "Detect logical section groupings if present (for example: 'Metadata', 'Templates', 'Settings', 'User Management'). "
174
+ "Return structured JSON ONLY, in this format:\n\n"
175
+ "[{\n"
176
+ " \"section\": \"Templates\",\n"
177
+ " \"screens\": [\n"
178
+ " {\"screen_name\": \"Document Templates\", \"html\": \"<div>...</div>\"}\n"
179
+ " ]\n"
180
+ "}]\n\n"
181
+ "Each screen should contain realistic PowerApps-like HTML components (cards, forms, grids, buttons, etc.). "
182
+ "Do not include explanations or text outside JSON."
183
  + "\n\n" + text
184
  )
185
  cleaned = re.sub(r"```(?:json)?|```", "", response.text.strip())
186
+ grouped_screens = json.loads(cleaned)
 
 
 
 
187
  except Exception as e:
188
+ print("⚠️ Gemini failed to create groupings:", e)
189
+ grouped_screens = [{
190
+ "section": "My Work",
191
+ "screens": [{"screen_name": "Dashboard", "html": "<h2>Error</h2><p>Unable to parse screens.</p>"}]
192
+ }]
193
 
194
+ return app_title, grouped_screens
195
 
196
  # -----------------------
197
  # Generate HTML + Image per Screen
198
  # -----------------------
199
+ def generate_mockups(app_title, grouped_screens):
200
+ image_paths = []
201
+ for group in grouped_screens:
202
+ for screen in group.get("screens", []):
203
+ label = screen.get("screen_name", "Screen")
204
+ html_content = screen.get("html", "<h2>Empty Screen</h2>")
 
 
 
 
 
 
205
 
206
+ sidebar_html = generate_sidebar(app_title, grouped_screens, active_label=label)
207
+ full_html = BASE_TEMPLATE.replace("{user_sidebar}", sidebar_html)\
208
+ .replace("{user_content}", html_content)\
209
+ .replace("{app_title}", app_title)
210
 
211
+ uid = str(uuid.uuid4())[:8]
212
+ html_path = f"mockup_{label.replace(' ', '_')}_{uid}.html"
213
+ img_path = f"mockup_{label.replace(' ', '_')}_{uid}.png"
214
+ with open(html_path, "w", encoding="utf-8") as f:
215
+ f.write(full_html)
216
+ hti.screenshot(html_file=html_path, save_as=img_path)
217
+ image_paths.append(img_path)
218
 
219
+ return image_paths
 
 
 
 
220
 
221
  # -----------------------
222
  # Full Pipeline with Retry Logic
 
224
  def generate_from_pdf(pdf_file):
225
  for attempt in range(3):
226
  try:
227
+ app_title, grouped_screens = analyze_business_pdf(pdf_file)
228
+ image_paths = generate_mockups(app_title, grouped_screens)
229
  return image_paths
230
  except Exception as e:
231
  if "429" in str(e) or "quota" in str(e).lower():
 
235
  raise e
236
  raise Exception("❌ Failed after 3 retries due to Gemini API quota limits.")
237
 
 
 
 
238
  # -----------------------
239
  # Gradio UI
240
  # -----------------------
241
  with gr.Blocks() as demo:
242
+ gr.Markdown("## 🧩 Intelligent PowerApps Mockup Generator (with Section Grouping)")
243
+ gr.Markdown("Upload a **Business Requirement PDF**. Gemini 2.5 Pro will infer grouped sections, screens, and layouts — then render PowerApps-style mockup images for each screen.")
244
  pdf_input = gr.File(label="📄 Upload Business Requirement PDF", file_types=[".pdf"])
245
+ generate_btn = gr.Button("🚀 Generate Mockups")
246
+ gallery_output = gr.Gallery(label="Generated Screens", show_label=True, columns=2)
247
+ generate_btn.click(fn=generate_from_pdf, inputs=pdf_input, outputs=gallery_output)
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  demo.launch(server_name="0.0.0.0", server_port=7860)