Rekham1110 commited on
Commit
2ea3c0a
·
verified ·
1 Parent(s): c605070

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -71
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- from PIL import Image
3
  import os
4
  from dotenv import load_dotenv
5
  from simple_salesforce import Salesforce
@@ -8,11 +8,18 @@ import hashlib
8
  import shutil
9
  import base64
10
  import pytz
11
- import time
12
- from PIL import ImageEnhance
 
 
 
 
 
 
 
13
 
14
  # Load environment variables
15
- print("Loading environment variables at", datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%Y-%m-%d %H:%M:%S IST"))
16
  load_dotenv()
17
  SF_USERNAME = os.getenv("SF_USERNAME")
18
  SF_PASSWORD = os.getenv("SF_PASSWORD")
@@ -20,55 +27,81 @@ SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
20
 
21
  # Validate Salesforce credentials
22
  if not all([SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN]):
 
23
  raise ValueError("Missing Salesforce credentials. Set SF_USERNAME, SF_PASSWORD, and SF_SECURITY_TOKEN in environment variables.")
24
- print("Salesforce credentials validated successfully.")
25
 
26
  # Initialize Salesforce connection
27
  try:
28
- print("Attempting Salesforce connection at", datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%Y-%m-%d %H:%M:%S IST"))
29
  sf = Salesforce(
30
  username=SF_USERNAME,
31
  password=SF_PASSWORD,
32
  security_token=SF_SECURITY_TOKEN,
33
  domain='login'
34
  )
35
- print("Salesforce connection established successfully.")
36
  except Exception as e:
37
- print(f"Salesforce connection failed: {str(e)}")
38
  raise
39
 
40
- print("Initializing application at", datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%Y-%m-%d %H:%M:%S IST"))
41
-
42
- # Updated Milestone Percentages
43
- milestone_percentage_map = {
44
- "Excavation and Foundation": 10,
45
- "Structural Framework": 40,
46
- "Roofing": 70,
47
- "Exterior Work": 85,
48
- "Interior Work": 95,
49
- "Final Completion": 100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
- # Adjust the timezone to your local timezone
53
  local_timezone = pytz.timezone("Asia/Kolkata")
54
 
55
- # Enhanced AI model prediction with stricter heuristic for completion
56
  def mock_ai_model(image):
 
57
  img = image.convert("RGB")
58
  max_size = 1024
59
  img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
60
 
61
- # Enhance contrast to detect features
62
  enhancer = ImageEnhance.Contrast(img)
63
  img_enhanced = enhancer.enhance(2.0)
 
 
64
 
65
- # Stricter heuristic for Final Completion (100%)
66
  img_data = list(img_enhanced.getdata())
67
  total_pixels = len(img_data)
68
  brightness_avg = sum(sum(pixel) / 3 for pixel in img_data) / total_pixels
69
  color_variation = max(max(pixel) - min(pixel) for pixel in img_data)
70
 
71
- # Edge detection (simple count of significant color changes)
72
  edge_count = 0
73
  width, height = img_enhanced.size
74
  for x in range(width - 1):
@@ -79,43 +112,61 @@ def mock_ai_model(image):
79
  edge_count += 1
80
  edge_ratio = edge_count / (width * height)
81
 
82
- # Final Completion criteria: high brightness, low variation, low edges (finished look)
83
  if brightness_avg > 220 and color_variation < 15 and edge_ratio < 0.05:
84
- return "Final Completion", 100, 0.95
85
-
86
- # Fallback to hash-based simulation
87
- img_bytes = img.tobytes()
88
- img_hash = int(hashlib.sha256(img_bytes).hexdigest(), 16)
89
-
90
- milestone_index = img_hash % len(milestone_percentage_map)
91
- milestone = list(milestone_percentage_map.keys())[milestone_index]
92
- confidence_raw = 0.85 + ((img_hash % 1000) / 1000) * (0.95 - 0.85)
93
- confidence_score = round(confidence_raw, 2)
94
-
95
- return milestone, milestone_percentage_map[milestone], confidence_score
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- # Image processing and Salesforce upload for multiple images
98
  def process_image(images, project_name, project_type):
 
99
  try:
100
  if not images:
101
- return "<p style='color: red;'>Error: Please upload at least one image to proceed.</p>", "Pending", "", "", 0
 
102
 
103
  if not project_name:
 
104
  return "<p style='color: red;'>Error: Project Name is required.</p>", "Pending", "", "", 0
105
 
106
  results = []
107
  image_urls = []
108
- milestone_confidences = []
109
  all_percentages = []
110
  all_milestones = set()
 
 
111
  dominant_milestone = None
112
  dominant_image_url = None
 
113
 
114
  for idx, image_path in enumerate(images):
115
  try:
116
  img = Image.open(image_path)
117
- milestone, percent_complete, confidence_score = mock_ai_model(img)
118
 
 
119
  upload_dir = "public_uploads"
120
  os.makedirs(upload_dir, exist_ok=True)
121
  unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
@@ -123,6 +174,7 @@ def process_image(images, project_name, project_type):
123
  saved_image_path = os.path.join(upload_dir, image_filename)
124
  shutil.copy(image_path, saved_image_path)
125
 
 
126
  with open(saved_image_path, 'rb') as image_file:
127
  image_data = base64.b64encode(image_file.read()).decode('utf-8')
128
 
@@ -137,29 +189,44 @@ def process_image(images, project_name, project_type):
137
  content_version_id = content_version_result['id']
138
  file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
139
  image_urls.append(file_url)
 
140
  except Exception as e:
 
141
  results.append(f"Image {idx+1}: Failed to upload to Salesforce - {str(e)}")
142
  continue
143
 
144
- if percent_complete > (all_percentages[0] if all_percentages else -1): # For dominant milestone
145
  dominant_milestone = milestone
146
  dominant_image_url = file_url
147
 
148
  all_percentages.append(percent_complete)
149
  all_milestones.add(milestone)
150
- milestone_confidences.append((milestone, confidence_score))
151
- results.append(f"Image {idx+1}: {milestone} - {percent_complete}% completion (Confidence: {confidence_score})")
 
 
 
 
 
 
 
 
152
 
153
  except Exception as e:
 
154
  results.append(f"Image {idx+1}: Error processing image - {str(e)}")
155
  continue
156
 
157
  if not results:
 
158
  return "<p style='color: red;'>Error: No images were successfully processed.</p>", "Failure", "", "", 0
159
 
160
- total_percent_complete = round(sum(all_percentages) / len(all_percentages), 2) if all_percentages else 0
161
  all_milestones_str = ", ".join(all_milestones)
 
 
162
 
 
163
  now = datetime.now(local_timezone)
164
  local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
165
 
@@ -170,37 +237,52 @@ def process_image(images, project_name, project_type):
170
  "Current_Milestone__c": all_milestones_str,
171
  "Last_Updated_On__c": local_time,
172
  "Upload_Status__c": "Success",
173
- "Comments__c": f"Project {project_name} with {total_percent_complete}% completion",
 
 
 
 
174
  "Last_Updated_Image__c": dominant_image_url or ""
175
  }
176
 
177
  try:
178
  sf.Construction__c.create(record)
 
179
  except Exception as e:
 
180
  return f"<p style='color: red;'>Error: Failed to update Salesforce - {str(e)}</p>", "Failure", "", "", 0
181
 
 
182
  output_html = "<div class='output'>"
183
  output_html += "<h3>Processing Results:</h3><ul>"
184
  for result in results:
185
- if 'Error' in result:
186
  output_html += f"<li class='error'>{result}</li>"
187
  else:
188
  output_html += f"<li class='success'>{result}</li>"
189
  output_html += "</ul>"
190
- output_html += f"<p><strong>Project Record Created:</strong> {project_name} (Total: {total_percent_complete}%)</p>"
191
- output_html += f"<p><strong>Note:</strong> Only the image with the highest completion percentage is stored in Salesforce due to field length constraints.</p>"
 
 
 
 
 
 
192
  output_html += "</div>"
193
 
194
- return output_html, "Success", "", f"Max Confidence: {max([conf for _, conf in milestone_confidences], default=0)}", f"{total_percent_complete}%"
195
 
196
  except Exception as e:
 
197
  return f"<p style='color: red;'>Error: {str(e)}</p>", "Failure", "", "", "0%"
198
 
199
- # Gradio UI with updated new styling
200
  with gr.Blocks(css="""
201
  .gradio-container {
202
  background-color: #f9f9f9;
203
  font-family: 'Roboto', sans-serif;
 
204
  }
205
  .title {
206
  color: #34495e;
@@ -212,8 +294,10 @@ with gr.Blocks(css="""
212
  }
213
  .gradio-row {
214
  text-align: center;
 
 
215
  }
216
- .gradio-container .output {
217
  text-align: left;
218
  margin-top: 20px;
219
  padding: 30px;
@@ -224,32 +308,32 @@ with gr.Blocks(css="""
224
  margin-left: auto;
225
  margin-right: auto;
226
  }
227
- .gradio-container .output h3 {
228
  color: #2c3e50;
229
  font-size: 22px;
230
  font-weight: bold;
231
  margin-bottom: 20px;
232
  }
233
- .gradio-container .output ul {
234
  list-style-type: none;
235
  padding: 0;
236
  }
237
- .gradio-container .output li {
238
  padding: 14px;
239
  margin-bottom: 18px;
240
  border-radius: 10px;
241
  font-size: 16px;
242
  transition: background-color 0.3s ease;
243
  }
244
- .gradio-container .output li.success {
245
  background-color: #27ae60;
246
  color: white;
247
  }
248
- .gradio-container .output li.error {
249
  background-color: #e74c3c;
250
  color: white;
251
  }
252
- .gradio-container .button {
253
  display: block;
254
  margin: 25px auto;
255
  background-color: #3498db;
@@ -261,23 +345,24 @@ with gr.Blocks(css="""
261
  font-size: 18px;
262
  transition: background-color 0.3s ease;
263
  }
264
- .gradio-container .button:hover {
265
  background-color: #2980b9;
266
  }
267
- .gradio-container .input {
268
  text-align: center;
 
 
269
  }
270
  """) as demo:
271
  gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
272
-
273
  with gr.Row():
274
  project_type_input = gr.Dropdown(label="Project Type", choices=["House", "Apartment"], value="House")
275
  project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
276
-
277
  image_input = gr.File(
278
  file_count="multiple",
279
  file_types=["image"],
280
- label="Upload Construction Site Photos (JPG/PNG, ≤ 20MB each)"
281
  )
282
  submit_button = gr.Button("Process Images")
283
  output_html = gr.HTML(label="Result")
@@ -288,12 +373,5 @@ with gr.Blocks(css="""
288
  outputs=[output_html]
289
  )
290
 
291
- print("Launching Gradio interface at", datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%Y-%m-%d %H:%M:%S IST"))
292
- try:
293
- demo.launch(share=True, debug=True) # Block and enable debug mode
294
- print("Gradio interface launched successfully.")
295
- while True: # Keep the script alive
296
- time.sleep(10)
297
- except Exception as e:
298
- print(f"Failed to launch Gradio interface: {str(e)}")
299
- raise
 
1
  import gradio as gr
2
+ from PIL import Image, ImageEnhance
3
  import os
4
  from dotenv import load_dotenv
5
  from simple_salesforce import Salesforce
 
8
  import shutil
9
  import base64
10
  import pytz
11
+ import logging
12
+
13
+ # Setup logging
14
+ logging.basicConfig(
15
+ filename="construction_analyzer.log",
16
+ level=logging.INFO,
17
+ format="%(asctime)s - %(levelname)s - %(message)s",
18
+ datefmt="%Y-%m-%d %H:%M:%S"
19
+ )
20
 
21
  # Load environment variables
22
+ logging.info("Loading environment variables")
23
  load_dotenv()
24
  SF_USERNAME = os.getenv("SF_USERNAME")
25
  SF_PASSWORD = os.getenv("SF_PASSWORD")
 
27
 
28
  # Validate Salesforce credentials
29
  if not all([SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN]):
30
+ logging.error("Missing Salesforce credentials")
31
  raise ValueError("Missing Salesforce credentials. Set SF_USERNAME, SF_PASSWORD, and SF_SECURITY_TOKEN in environment variables.")
32
+ logging.info("Salesforce credentials validated successfully")
33
 
34
  # Initialize Salesforce connection
35
  try:
36
+ logging.info("Attempting Salesforce connection")
37
  sf = Salesforce(
38
  username=SF_USERNAME,
39
  password=SF_PASSWORD,
40
  security_token=SF_SECURITY_TOKEN,
41
  domain='login'
42
  )
43
+ logging.info("Salesforce connection established successfully")
44
  except Exception as e:
45
+ logging.error(f"Salesforce connection failed: {str(e)}")
46
  raise
47
 
48
+ # Milestone definitions with completed and pending tasks
49
+ milestone_data = {
50
+ "Excavation and Foundation": {
51
+ "percentage": 10,
52
+ "completed": ["Site clearing", "Excavation", "Foundation pouring"],
53
+ "pending": ["Structural framework", "Roofing", "Exterior work", "Interior work", "Final inspection"]
54
+ },
55
+ "Structural Framework": {
56
+ "percentage": 40,
57
+ "completed": ["Site clearing", "Excavation", "Foundation pouring", "Structural columns and beams"],
58
+ "pending": ["Roofing", "Exterior work", "Interior work", "Final inspection"]
59
+ },
60
+ "Roofing": {
61
+ "percentage": 70,
62
+ "completed": ["Site clearing", "Excavation", "Foundation pouring", "Structural columns and beams", "Roof installation"],
63
+ "pending": ["Exterior work", "Interior work", "Final inspection"]
64
+ },
65
+ "Exterior Work": {
66
+ "percentage": 85,
67
+ "completed": ["Site clearing", "Excavation", "Foundation pouring", "Structural columns and beams", "Roof installation", "Exterior walls", "Windows and doors"],
68
+ "pending": ["Interior work", "Final inspection"]
69
+ },
70
+ "Interior Work": {
71
+ "percentage": 95,
72
+ "completed": ["Site clearing", "Excavation", "Foundation pouring", "Structural columns and beams", "Roof installation", "Exterior walls", "Windows and doors", "Interior plumbing", "Electrical work", "Drywall and painting"],
73
+ "pending": ["Final inspection"]
74
+ },
75
+ "Final Completion": {
76
+ "percentage": 100,
77
+ "completed": ["Site clearing", "Excavation", "Foundation pouring", "Structural columns and beams", "Roof installation", "Exterior walls", "Windows and doors", "Interior plumbing", "Electrical work", "Drywall and painting", "Final inspection"],
78
+ "pending": []
79
+ }
80
  }
81
 
82
+ # Adjust the timezone to Asia/Kolkata
83
  local_timezone = pytz.timezone("Asia/Kolkata")
84
 
85
+ # Enhanced mock AI model simulating Grok-like analysis
86
  def mock_ai_model(image):
87
+ logging.info("Analyzing image for construction progress")
88
  img = image.convert("RGB")
89
  max_size = 1024
90
  img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
91
 
92
+ # Enhance contrast and brightness for feature detection
93
  enhancer = ImageEnhance.Contrast(img)
94
  img_enhanced = enhancer.enhance(2.0)
95
+ enhancer = ImageEnhance.Brightness(img_enhanced)
96
+ img_enhanced = enhancer.enhance(1.2)
97
 
98
+ # Analyze image features
99
  img_data = list(img_enhanced.getdata())
100
  total_pixels = len(img_data)
101
  brightness_avg = sum(sum(pixel) / 3 for pixel in img_data) / total_pixels
102
  color_variation = max(max(pixel) - min(pixel) for pixel in img_data)
103
 
104
+ # Edge detection
105
  edge_count = 0
106
  width, height = img_enhanced.size
107
  for x in range(width - 1):
 
112
  edge_count += 1
113
  edge_ratio = edge_count / (width * height)
114
 
115
+ # Simulate Grok-like reasoning for milestone detection
116
  if brightness_avg > 220 and color_variation < 15 and edge_ratio < 0.05:
117
+ milestone = "Final Completion"
118
+ confidence = 0.95
119
+ elif brightness_avg > 180 and edge_ratio < 0.1:
120
+ milestone = "Interior Work"
121
+ confidence = 0.90
122
+ elif brightness_avg > 150 and edge_ratio < 0.2:
123
+ milestone = "Exterior Work"
124
+ confidence = 0.88
125
+ elif brightness_avg > 120 and edge_ratio < 0.3:
126
+ milestone = "Roofing"
127
+ confidence = 0.85
128
+ elif brightness_avg > 90 and edge_ratio < 0.4:
129
+ milestone = "Structural Framework"
130
+ confidence = 0.82
131
+ else:
132
+ milestone = "Excavation and Foundation"
133
+ confidence = 0.80
134
+
135
+ completed_tasks = milestone_data[milestone]["completed"]
136
+ pending_tasks = milestone_data[milestone]["pending"]
137
+ percentage = milestone_data[milestone]["percentage"]
138
+
139
+ logging.info(f"Image analyzed: Milestone={milestone}, Percentage={percentage}, Confidence={confidence}")
140
+ return milestone, percentage, confidence, completed_tasks, pending_tasks
141
 
142
+ # Process images and upload to Salesforce
143
  def process_image(images, project_name, project_type):
144
+ logging.info(f"Processing {len(images)} images for project {project_name}")
145
  try:
146
  if not images:
147
+ logging.warning("No images uploaded")
148
+ return "<p style='color: red;'>Error: Please upload at least one image.</p>", "Pending", "", "", 0
149
 
150
  if not project_name:
151
+ logging.warning("Project name missing")
152
  return "<p style='color: red;'>Error: Project Name is required.</p>", "Pending", "", "", 0
153
 
154
  results = []
155
  image_urls = []
 
156
  all_percentages = []
157
  all_milestones = set()
158
+ all_completed_tasks = set()
159
+ all_pending_tasks = set()
160
  dominant_milestone = None
161
  dominant_image_url = None
162
+ max_confidence = 0
163
 
164
  for idx, image_path in enumerate(images):
165
  try:
166
  img = Image.open(image_path)
167
+ milestone, percent_complete, confidence, completed_tasks, pending_tasks = mock_ai_model(img)
168
 
169
+ # Save image locally
170
  upload_dir = "public_uploads"
171
  os.makedirs(upload_dir, exist_ok=True)
172
  unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
 
174
  saved_image_path = os.path.join(upload_dir, image_filename)
175
  shutil.copy(image_path, saved_image_path)
176
 
177
+ # Upload to Salesforce
178
  with open(saved_image_path, 'rb') as image_file:
179
  image_data = base64.b64encode(image_file.read()).decode('utf-8')
180
 
 
189
  content_version_id = content_version_result['id']
190
  file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
191
  image_urls.append(file_url)
192
+ logging.info(f"Image {idx+1} uploaded to Salesforce: {file_url}")
193
  except Exception as e:
194
+ logging.error(f"Image {idx+1} upload failed: {str(e)}")
195
  results.append(f"Image {idx+1}: Failed to upload to Salesforce - {str(e)}")
196
  continue
197
 
198
+ if percent_complete > (all_percentages[0] if all_percentages else -1):
199
  dominant_milestone = milestone
200
  dominant_image_url = file_url
201
 
202
  all_percentages.append(percent_complete)
203
  all_milestones.add(milestone)
204
+ all_completed_tasks.update(completed_tasks)
205
+ all_pending_tasks.update(pending_tasks)
206
+ if confidence > max_confidence:
207
+ max_confidence = confidence
208
+
209
+ results.append(
210
+ f"Image {idx+1}: {milestone} - {percent_complete}% completion (Confidence: {confidence})<br>"
211
+ f"<strong>Completed:</strong> {', '.join(completed_tasks)}<br>"
212
+ f"<strong>Pending:</strong> {', '.join(pending_tasks) if pending_tasks else 'None'}"
213
+ )
214
 
215
  except Exception as e:
216
+ logging.error(f"Image {idx+1} processing failed: {str(e)}")
217
  results.append(f"Image {idx+1}: Error processing image - {str(e)}")
218
  continue
219
 
220
  if not results:
221
+ logging.warning("No images processed successfully")
222
  return "<p style='color: red;'>Error: No images were successfully processed.</p>", "Failure", "", "", 0
223
 
224
+ total_percent_complete = round(sum(all_percentages) / len(all_percentages), 2)
225
  all_milestones_str = ", ".join(all_milestones)
226
+ all_completed_tasks_str = ", ".join(sorted(all_completed_tasks))
227
+ all_pending_tasks_str = ", ".join(sorted(all_pending_tasks)) if all_pending_tasks else "None"
228
 
229
+ # Create Salesforce record
230
  now = datetime.now(local_timezone)
231
  local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
232
 
 
237
  "Current_Milestone__c": all_milestones_str,
238
  "Last_Updated_On__c": local_time,
239
  "Upload_Status__c": "Success",
240
+ "Comments__c": (
241
+ f"Project {project_name} at {total_percent_complete}% completion. "
242
+ f"Completed tasks: {all_completed_tasks_str}. "
243
+ f"Pending tasks: {all_pending_tasks_str}."
244
+ ),
245
  "Last_Updated_Image__c": dominant_image_url or ""
246
  }
247
 
248
  try:
249
  sf.Construction__c.create(record)
250
+ logging.info(f"Salesforce record created for project {project_name}")
251
  except Exception as e:
252
+ logging.error(f"Failed to create Salesforce record: {str(e)}")
253
  return f"<p style='color: red;'>Error: Failed to update Salesforce - {str(e)}</p>", "Failure", "", "", 0
254
 
255
+ # Format output
256
  output_html = "<div class='output'>"
257
  output_html += "<h3>Processing Results:</h3><ul>"
258
  for result in results:
259
+ if 'Error' in result or 'Failed' in result:
260
  output_html += f"<li class='error'>{result}</li>"
261
  else:
262
  output_html += f"<li class='success'>{result}</li>"
263
  output_html += "</ul>"
264
+ output_html += f"<h3>Project Summary:</h3>"
265
+ output_html += f"<p><strong>Project:</strong> {project_name} ({project_type})</p>"
266
+ output_html += f"<p><strong>Total Completion:</strong> {total_percent_complete}%</p>"
267
+ output_html += f"<p><strong>Milestones Detected:</strong> {all_milestones_str}</p>"
268
+ output_html += f"<p><strong>Completed Tasks:</strong> {all_completed_tasks_str}</p>"
269
+ output_html += f"<p><strong>Pending Tasks:</strong> {all_pending_tasks_str}</p>"
270
+ output_html += f"<p><strong>Max Confidence:</strong> {max_confidence}</p>"
271
+ output_html += f"<p><strong>Note:</strong> Only the image with the highest completion percentage is stored in Salesforce.</p>"
272
  output_html += "</div>"
273
 
274
+ return output_html, "Success", "", f"Max Confidence: {max_confidence}", f"{total_percent_complete}%"
275
 
276
  except Exception as e:
277
+ logging.error(f"Processing failed: {str(e)}")
278
  return f"<p style='color: red;'>Error: {str(e)}</p>", "Failure", "", "", "0%"
279
 
280
+ # Gradio UI
281
  with gr.Blocks(css="""
282
  .gradio-container {
283
  background-color: #f9f9f9;
284
  font-family: 'Roboto', sans-serif;
285
+ padding: 20px;
286
  }
287
  .title {
288
  color: #34495e;
 
294
  }
295
  .gradio-row {
296
  text-align: center;
297
+ max-width: 800px;
298
+ margin: 0 auto;
299
  }
300
+ .output {
301
  text-align: left;
302
  margin-top: 20px;
303
  padding: 30px;
 
308
  margin-left: auto;
309
  margin-right: auto;
310
  }
311
+ .output h3 {
312
  color: #2c3e50;
313
  font-size: 22px;
314
  font-weight: bold;
315
  margin-bottom: 20px;
316
  }
317
+ .output ul {
318
  list-style-type: none;
319
  padding: 0;
320
  }
321
+ .output li {
322
  padding: 14px;
323
  margin-bottom: 18px;
324
  border-radius: 10px;
325
  font-size: 16px;
326
  transition: background-color 0.3s ease;
327
  }
328
+ .output li.success {
329
  background-color: #27ae60;
330
  color: white;
331
  }
332
+ .output li.error {
333
  background-color: #e74c3c;
334
  color: white;
335
  }
336
+ .button {
337
  display: block;
338
  margin: 25px auto;
339
  background-color: #3498db;
 
345
  font-size: 18px;
346
  transition: background-color 0.3s ease;
347
  }
348
+ .button:hover {
349
  background-color: #2980b9;
350
  }
351
+ .input {
352
  text-align: center;
353
+ max-width: 800px;
354
+ margin: 0 auto;
355
  }
356
  """) as demo:
357
  gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
 
358
  with gr.Row():
359
  project_type_input = gr.Dropdown(label="Project Type", choices=["House", "Apartment"], value="House")
360
  project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
361
+
362
  image_input = gr.File(
363
  file_count="multiple",
364
  file_types=["image"],
365
+ label="Upload Construction Site Photos (JPG/PNG, ≤20MB each)"
366
  )
367
  submit_button = gr.Button("Process Images")
368
  output_html = gr.HTML(label="Result")
 
373
  outputs=[output_html]
374
  )
375
 
376
+ logging.info("Launching Gradio interface")
377
+ demo.launch()