ChristianQ commited on
Commit
7c0b56e
·
1 Parent(s): 6990ba3

Updated app.py, possible fix for coding module integration

Browse files
Files changed (1) hide show
  1. app.py +142 -110
app.py CHANGED
@@ -5,38 +5,32 @@ import traceback
5
 
6
  import gradio as gr
7
  from fastapi import FastAPI, File, UploadFile
 
8
  from fastapi.middleware.cors import CORSMiddleware
9
  from fastapi.responses import JSONResponse, FileResponse
10
 
11
  from visualization import process_wireframe
12
  from CodingModule import HTMLGenerator
13
 
14
-
15
- # ============================================================
16
- # DIRECTORIES
17
- # ============================================================
18
-
19
- TEMP_DIR = "./temp"
20
- OUTPUT_DIR = "./output"
21
-
22
- os.makedirs(TEMP_DIR, exist_ok=True)
23
- os.makedirs(OUTPUT_DIR, exist_ok=True)
24
-
25
-
26
- # ============================================================
27
- # FASTAPI BACKEND
28
- # ============================================================
29
-
30
  api = FastAPI()
31
 
32
  api.add_middleware(
33
  CORSMiddleware,
34
- allow_origins=["*"], # change to your Firebase domain later
35
  allow_credentials=True,
36
  allow_methods=["*"],
37
  allow_headers=["*"],
38
  )
39
 
 
 
 
 
 
 
40
 
41
  @api.get("/")
42
  def health_check():
@@ -49,24 +43,9 @@ async def process_wireframe_api(image: UploadFile = File(...)):
49
  temp_path = os.path.join(TEMP_DIR, f"{file_id}_{image.filename}")
50
 
51
  try:
52
- # ----------------------------------------------------
53
- # SAVE UPLOADED IMAGE
54
- # ----------------------------------------------------
55
  with open(temp_path, "wb") as f:
56
  shutil.copyfileobj(image.file, f)
57
 
58
- file_size = os.path.getsize(temp_path)
59
- print("Uploaded file size:", file_size)
60
-
61
- if file_size == 0:
62
- return JSONResponse(
63
- status_code=400,
64
- content={"error": "Uploaded file is empty — check frontend upload format."}
65
- )
66
-
67
- # ----------------------------------------------------
68
- # RUN DETECTION PIPELINE
69
- # ----------------------------------------------------
70
  results = process_wireframe(
71
  image_path=temp_path,
72
  save_json=True,
@@ -74,31 +53,26 @@ async def process_wireframe_api(image: UploadFile = File(...)):
74
  show_visualization=False
75
  )
76
 
77
- if not results or not results.get("normalized_elements"):
78
  return JSONResponse(
79
  status_code=400,
80
  content={"error": "No elements detected"}
81
  )
82
-
83
  json_path = results.get("json_path")
84
-
85
- # ----------------------------------------------------
86
- # GENERATE STYLED HTML
87
- # ----------------------------------------------------
88
  try:
 
89
  html_filename = f"{file_id}_styled.html"
90
  html_path = os.path.join(OUTPUT_DIR, html_filename)
91
-
92
- generator = HTMLGenerator(json_path)
93
- generator.generate_html(html_path)
94
-
95
  except Exception as e:
96
- print("HTML generation error:", e)
97
  html_path = results.get("html_path")
98
-
99
  return {
100
  "success": True,
101
- "file_id": file_id,
102
  "json_path": json_path,
103
  "html_path": html_path,
104
  "total_elements": len(results["normalized_elements"])
@@ -115,97 +89,155 @@ async def process_wireframe_api(image: UploadFile = File(...)):
115
  if os.path.exists(temp_path):
116
  os.remove(temp_path)
117
 
118
-
119
- # ============================================================
120
- # SERVE GENERATED FILES (HTML / JSON)
121
- # ============================================================
122
-
123
- @api.get("/output/{filename}")
124
- async def serve_output(filename: str):
125
- path = os.path.join(OUTPUT_DIR, filename)
126
- if not os.path.exists(path):
127
- return JSONResponse(status_code=404,
128
- content={"error": "File not found"})
129
-
130
- if filename.endswith(".html"):
131
- return FileResponse(path, media_type="text/html")
132
-
133
- if filename.endswith(".json"):
134
- return FileResponse(path, media_type="application/json")
135
-
136
- return FileResponse(path)
137
-
138
-
139
- # ============================================================
140
- # GRADIO FRONTEND (for HF testing only)
141
- # ============================================================
142
-
 
 
 
143
  def gradio_process(image):
144
-
 
 
 
145
  if image is None:
146
- return "Upload an image first", None, None
147
-
148
  file_id = str(uuid.uuid4())
149
  temp_path = os.path.join(TEMP_DIR, f"{file_id}.png")
150
-
151
- image.save(temp_path)
 
 
 
152
 
153
  try:
154
  results = process_wireframe(
155
  image_path=temp_path,
156
  save_json=True,
157
- save_html=True,
158
  show_visualization=False
159
  )
160
 
161
- if not results or not results.get("normalized_elements"):
162
  return "No elements detected", None, None
163
 
164
  json_path = results.get("json_path")
165
-
 
166
  try:
 
167
  html_filename = f"{file_id}_styled.html"
168
  html_path = os.path.join(OUTPUT_DIR, html_filename)
169
-
170
- generator = HTMLGenerator(json_path)
171
- generator.generate_html(html_path)
172
-
173
- except Exception:
174
  html_path = results.get("html_path")
175
-
176
- return (
177
- f"Detected {len(results['normalized_elements'])} elements",
178
- json_path,
179
- html_path
180
- )
 
181
 
182
  except Exception as e:
183
  traceback.print_exc()
184
- return f"Error: {e}", None, None
185
 
186
  finally:
187
- os.remove(temp_path)
188
-
189
 
190
- with gr.Blocks(title="Wireframe Layout Normalizer") as demo:
 
191
  gr.Markdown("# Wireframe Layout Normalizer")
192
-
193
- inp = gr.Image(type="pil")
194
- btn = gr.Button("Process")
195
- out1 = gr.Textbox(label="Status")
196
- out2 = gr.File(label="JSON")
197
- out3 = gr.File(label="HTML")
198
-
199
- btn.click(gradio_process, inp, [out1, out2, out3])
200
-
201
-
202
- # ============================================================
203
- # MOUNT FASTAPI + GRADIO
204
- # ============================================================
205
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  app = gr.mount_gradio_app(api, demo, path="/")
207
 
208
-
209
- if __name__ == "__main__":
210
  import uvicorn
211
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
5
 
6
  import gradio as gr
7
  from fastapi import FastAPI, File, UploadFile
8
+ from fastapi.responses import JSONResponse
9
  from fastapi.middleware.cors import CORSMiddleware
10
  from fastapi.responses import JSONResponse, FileResponse
11
 
12
  from visualization import process_wireframe
13
  from CodingModule import HTMLGenerator
14
 
15
+ # -----------------------------------------------------------------------------
16
+ # FASTAPI (for Firebase / programmatic access)
17
+ # -----------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  api = FastAPI()
19
 
20
  api.add_middleware(
21
  CORSMiddleware,
22
+ allow_origins=["*"], #Replace with the Firebase domain
23
  allow_credentials=True,
24
  allow_methods=["*"],
25
  allow_headers=["*"],
26
  )
27
 
28
+ TEMP_DIR = "./temp"
29
+ OUTPUT_DIR = "./output"
30
+
31
+ os.makedirs(TEMP_DIR, exist_ok=True)
32
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
33
+
34
 
35
  @api.get("/")
36
  def health_check():
 
43
  temp_path = os.path.join(TEMP_DIR, f"{file_id}_{image.filename}")
44
 
45
  try:
 
 
 
46
  with open(temp_path, "wb") as f:
47
  shutil.copyfileobj(image.file, f)
48
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  results = process_wireframe(
50
  image_path=temp_path,
51
  save_json=True,
 
53
  show_visualization=False
54
  )
55
 
56
+ if not results or not results.get('normalized_elements'):
57
  return JSONResponse(
58
  status_code=400,
59
  content={"error": "No elements detected"}
60
  )
61
+
62
  json_path = results.get("json_path")
63
+
64
+ #Generate styled HTML using CodingModule
 
 
65
  try:
66
+ html_generator = HTMLGenerator(json_path)
67
  html_filename = f"{file_id}_styled.html"
68
  html_path = os.path.join(OUTPUT_DIR, html_filename)
69
+ html_generator.generate_html(html_path)
 
 
 
70
  except Exception as e:
71
+ print(f"Warning: Could not generate styled HTML: {e}")
72
  html_path = results.get("html_path")
73
+
74
  return {
75
  "success": True,
 
76
  "json_path": json_path,
77
  "html_path": html_path,
78
  "total_elements": len(results["normalized_elements"])
 
89
  if os.path.exists(temp_path):
90
  os.remove(temp_path)
91
 
92
+ #Endpoint to serve generated HTML files
93
+ @api.get("/view-html/{file_id}")
94
+ async def serve_output_file(filename: str):
95
+ file_path = os.path.join(OUTPUT_DIR, filename)
96
+ if os.path.exists(file_path):
97
+ if filename.endswith('.html'):
98
+ return FileResponse(file_path, media_type="text/html")
99
+ elif filename.endswith('.json'):
100
+ return FileResponse(file_path, media_type="application/json")
101
+ else:
102
+ return FileResponse(file_path)
103
+ return JSONResponse(status_code=404, content={"error": "File not Found"})
104
+
105
+ # # Legacy endpoint for just for backward compatibility
106
+ # @api.get("/view-html/{file_id}")
107
+ # async def view_html(file_id: str):
108
+ # html_path = os.path.join(OUTPUT_DIR, f"{file_id}.html")
109
+ # if os.path.exists(html_path):
110
+ # return FileResponse(html_path, media_type="text/html")
111
+ # # Try with _styled suffix
112
+ # html_path = os.path.join(OUTPUT_DIR, f"{file_id}_styled.html")
113
+ # if os.path.exists(html_path):
114
+ # return FileResponse(html_path, media_type="text/html")
115
+ # return JSONResponse(status_code=404, content={"error": "File not found"})
116
+
117
+ # -----------------------------------------------------------------------------
118
+ # GRADIO (for Hugging Face UI)
119
+ # -----------------------------------------------------------------------------
120
  def gradio_process(image):
121
+ """
122
+ Gradio passes a PIL Image.
123
+ We save it temporarily and reuse the SAME pipeline.
124
+ """
125
  if image is None:
126
+ return "Please upload an image", None, None
127
+
128
  file_id = str(uuid.uuid4())
129
  temp_path = os.path.join(TEMP_DIR, f"{file_id}.png")
130
+
131
+ try:
132
+ image.save(temp_path)
133
+ except Exception as e:
134
+ return f"Error saving image: {str(e)}", None, None
135
 
136
  try:
137
  results = process_wireframe(
138
  image_path=temp_path,
139
  save_json=True,
140
+ save_html=False,
141
  show_visualization=False
142
  )
143
 
144
+ if not results or not results.get('normalized_elements'):
145
  return "No elements detected", None, None
146
 
147
  json_path = results.get("json_path")
148
+
149
+ #Generate styled HTML using CodingModule
150
  try:
151
+ html_generator = HTMLGenerator(json_path)
152
  html_filename = f"{file_id}_styled.html"
153
  html_path = os.path.join(OUTPUT_DIR, html_filename)
154
+ html_generator.generate_html(html_path)
155
+ except Exception as e:
156
+ print(f"Warning: Could not generate styled HTML: {e}")
157
+ traceback.print_exc()
 
158
  html_path = results.get("html_path")
159
+
160
+ num_elements = len(results['normalized_elements'])
161
+ status_msg = f"Successfully detected {num_elements} elemnts\n"
162
+ status_msg += f"JSON: {os.path.basename(json_path)}\n"
163
+ status_msg += f"HTML: {os.path.basename(html_path)}"
164
+
165
+ return status_msg, json_path, html_path
166
 
167
  except Exception as e:
168
  traceback.print_exc()
169
+ return f"Error: {str(e)}", None, None
170
 
171
  finally:
172
+ if os.path.exists(temp_path):
173
+ os.remove(temp_path)
174
 
175
+ # Enhanced Gradio interface with better UI
176
+ with gr.Blocks(theme=gr.themes.Soft(), title="Wireframe Layout Normalizer") as demo:
177
  gr.Markdown("# Wireframe Layout Normalizer")
178
+ gr.Markdown("Upload a wireframe image to extract and normalize UI layout elements")
179
+
180
+ gr.Markdown("""
181
+ ### Academic Research Project made by SeroTech 2025-2026 Holy Angel University
182
+ This tool is part of a thesis project. Upload your wireframe sketch to automatically:
183
+ - Detect UI elements (buttons, text fields, images, etc.)
184
+ - Normalize positions and alignments
185
+ - Export structured JSON data
186
+ - Generate beautiful HTML preview
187
+ """)
188
+
189
+ with gr.Row():
190
+ with gr.Column(scale=1):
191
+ image_input = gr.Image(type="pil", label="Upload Wireframe Image")
192
+ process_btn = gr.Button("Process Wireframe", variant="primary", size="lg")
193
+
194
+ gr.Markdown("""
195
+ #### Tips:
196
+ - Upload clear wireframe sketches
197
+ - PNG, JPG, or JPEG formats supported
198
+ - Higher resolution = better detection
199
+ """)
200
+
201
+ with gr.Column(scale=1):
202
+ status_output = gr.Textbox(
203
+ label="Processing Status",
204
+ lines=3,
205
+ placeholder="Upload an image and click Process to begin..."
206
+ )
207
+ json_output = gr.File(label="Normalized JSON Output")
208
+ html_output = gr.File(label="Generated HTML Preview")
209
+
210
+ process_btn.click(
211
+ fn=gradio_process,
212
+ inputs=image_input,
213
+ outputs=[status_output, json_output, html_output]
214
+ )
215
+
216
+ gr.Markdown("""
217
+ ---
218
+ ### How to use:
219
+ 1. *Upload* a wireframe image (hand-drawn or digital)
220
+ 2. *Click* "Process Wireframe" and wait for processing
221
+ 3. *Download* the JSON file for structured data
222
+ 4. *Download* the HTML file to see a styled preview
223
+
224
+ ### Output Files:
225
+ - *JSON*: Contains all detected elements with positions, types, and grid data
226
+ - *HTML*: Beautiful, responsive preview with modern styling
227
+
228
+ ### Technical Details:
229
+ - Model: TensorFlow-based object detection
230
+ - Grid System: 24-column responsive layout
231
+ - Supported Elements: Buttons, Checkboxes, Text Fields, Images, Paragraphs, Text, Navbars
232
+ """)
233
+
234
+
235
+ # -----------------------------------------------------------------------------
236
+ # ENTRY POINT (THIS IS IMPORTANT)
237
+ # -----------------------------------------------------------------------------
238
  app = gr.mount_gradio_app(api, demo, path="/")
239
 
240
+ if _name_ == "_main_":
 
241
  import uvicorn
242
+ # For local testing
243
+ uvicorn.run(app, host="0.0.0.0", port=7860)