neerajkalyank commited on
Commit
9470eb1
·
verified ·
1 Parent(s): 18671bf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -112
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- from PIL import Image, ImageEnhance, ImageStat
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
- import statistics
 
 
 
 
 
 
 
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,79 @@ 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 to simulate Grok LLM
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 and brightness for feature detection
62
- enhancer_contrast = ImageEnhance.Contrast(img)
63
- img_enhanced = enhancer_contrast.enhance(1.5)
64
- enhancer_brightness = ImageEnhance.Brightness(img_enhanced)
65
- img_enhanced = enhancer_brightness.enhance(1.2)
66
 
67
- # Analyze image statistics
68
- stats = ImageStat.Stat(img_enhanced)
69
- mean_color = stats.mean # Average RGB values
70
- stddev_color = stats.stddev # Standard deviation for color variation
71
- total_pixels = img_enhanced.size[0] * img_enhanced.size[1]
72
 
73
  # Edge detection
74
  edge_count = 0
@@ -77,73 +108,65 @@ def mock_ai_model(image):
77
  for y in range(height - 1):
78
  r, g, b = img_enhanced.getpixel((x, y))
79
  r_next, g_next, b_next = img_enhanced.getpixel((x + 1, y + 1))
80
- if abs(r - r_next) > 60 or abs(g - g_next) > 60 or abs(b - b_next) > 60:
81
  edge_count += 1
82
- edge_ratio = edge_count / total_pixels
83
 
84
- # Color distribution analysis
85
- color_variation = sum(stddev_color) / 3 # Average standard deviation across RGB
86
- brightness_avg = sum(mean_color) / 3 # Average brightness
87
-
88
- # Simulate Grok's reasoning for milestone detection
89
- reasoning = []
90
- milestone = "Excavation and Foundation" # Default
91
- confidence_score = 0.85
92
- percent_complete = milestone_percentage_map[milestone]
93
-
94
- # Rules to simulate Grok's intelligent analysis
95
- if brightness_avg > 220 and color_variation < 10 and edge_ratio < 0.03:
96
  milestone = "Final Completion"
97
- confidence_score = 0.95
98
- reasoning.append("The image shows high brightness, low color variation, and minimal edges, indicating a finished, polished construction site.")
99
- elif edge_ratio > 0.2 and color_variation > 50:
100
- milestone = "Excavation and Foundation"
101
- confidence_score = 0.90
102
- reasoning.append("High edge density and significant color variation suggest an early-stage site with exposed earth and equipment.")
103
- elif mean_color[2] > 150 and edge_ratio < 0.15: # High blue (sky) and moderate edges
104
- milestone = "Roofing"
105
- confidence_score = 0.88
106
- reasoning.append("Presence of blue tones (likely sky) and moderate edges indicate roofing stage with open structures.")
107
- elif color_variation > 30 and edge_ratio < 0.1:
108
- milestone = "Exterior Work"
109
- confidence_score = 0.87
110
- reasoning.append("Moderate color variation and low edges suggest exterior finishing, like painting or cladding.")
111
- elif brightness_avg > 180 and color_variation < 20:
112
  milestone = "Interior Work"
113
- confidence_score = 0.89
114
- reasoning.append("High brightness and low color variation indicate interior work with finished surfaces.")
115
- else:
 
 
 
 
 
116
  milestone = "Structural Framework"
117
- confidence_score = 0.86
118
- reasoning.append("Defaulting to structural framework due to mixed features, likely indicating concrete or steel structures.")
 
 
119
 
120
- percent_complete = milestone_percentage_map[milestone]
121
- reasoning_str = " ".join(reasoning)
122
- return milestone, percent_complete, confidence_score, reasoning_str
 
 
 
123
 
124
- # Image processing and Salesforce upload for multiple images
125
  def process_image(images, project_name, project_type):
 
126
  try:
127
  if not images:
128
- return "<p style='color: red;'>Error: Please upload at least one image to proceed.</p>", "Pending", "", "", 0
 
129
 
130
  if not project_name:
 
131
  return "<p style='color: red;'>Error: Project Name is required.</p>", "Pending", "", "", 0
132
 
133
  results = []
134
  image_urls = []
135
- milestone_confidences = []
136
  all_percentages = []
137
  all_milestones = set()
 
 
138
  dominant_milestone = None
139
  dominant_image_url = None
140
- all_reasonings = []
141
 
142
  for idx, image_path in enumerate(images):
143
  try:
144
  img = Image.open(image_path)
145
- milestone, percent_complete, confidence_score, reasoning = mock_ai_model(img)
146
 
 
147
  upload_dir = "public_uploads"
148
  os.makedirs(upload_dir, exist_ok=True)
149
  unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
@@ -151,6 +174,7 @@ def process_image(images, project_name, project_type):
151
  saved_image_path = os.path.join(upload_dir, image_filename)
152
  shutil.copy(image_path, saved_image_path)
153
 
 
154
  with open(saved_image_path, 'rb') as image_file:
155
  image_data = base64.b64encode(image_file.read()).decode('utf-8')
156
 
@@ -165,32 +189,44 @@ def process_image(images, project_name, project_type):
165
  content_version_id = content_version_result['id']
166
  file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
167
  image_urls.append(file_url)
 
168
  except Exception as e:
 
169
  results.append(f"Image {idx+1}: Failed to upload to Salesforce - {str(e)}")
170
- all_reasonings.append(f"Error: Failed to upload image {idx+1}.")
171
  continue
172
 
173
- if percent_complete > (all_percentages[0] if all_percentages else -1): # For dominant milestone
174
  dominant_milestone = milestone
175
  dominant_image_url = file_url
176
 
177
  all_percentages.append(percent_complete)
178
  all_milestones.add(milestone)
179
- milestone_confidences.append((milestone, confidence_score))
180
- all_reasonings.append(f"Image {idx+1}: {milestone} - {percent_complete}% completion (Confidence: {confidence_score}). Reasoning: {reasoning}")
181
- results.append(f"Image {idx+1}: {milestone} - {percent_complete}% completion (Confidence: {confidence_score})")
 
 
 
 
 
 
 
182
 
183
  except Exception as e:
 
184
  results.append(f"Image {idx+1}: Error processing image - {str(e)}")
185
- all_reasonings.append(f"Error: Failed to process image {idx+1} - {str(e)}")
186
  continue
187
 
188
  if not results:
 
189
  return "<p style='color: red;'>Error: No images were successfully processed.</p>", "Failure", "", "", 0
190
 
191
- total_percent_complete = round(statistics.mean(all_percentages), 2) if all_percentages else 0
192
  all_milestones_str = ", ".join(all_milestones)
 
 
193
 
 
194
  now = datetime.now(local_timezone)
195
  local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
196
 
@@ -201,43 +237,52 @@ def process_image(images, project_name, project_type):
201
  "Current_Milestone__c": all_milestones_str,
202
  "Last_Updated_On__c": local_time,
203
  "Upload_Status__c": "Success",
204
- "Comments__c": f"Project {project_name} with {total_percent_complete}% completion",
 
 
 
 
205
  "Last_Updated_Image__c": dominant_image_url or ""
206
  }
207
 
208
  try:
209
  sf.Construction__c.create(record)
 
210
  except Exception as e:
 
211
  return f"<p style='color: red;'>Error: Failed to update Salesforce - {str(e)}</p>", "Failure", "", "", 0
212
 
213
- # Grok-like conversational summary
214
- grok_summary = f"Hello! I've analyzed the images for your project '{project_name}' ({project_type}). Here's what I found:\n"
215
- grok_summary += "\n".join(all_reasonings)
216
- grok_summary += f"\n\nOverall, the project is approximately {total_percent_complete}% complete, based on an average of the analyzed images. The milestones detected include: {all_milestones_str}. The image with the highest completion ({dominant_milestone}) has been stored in Salesforce for reference. Let me know if you'd like further insights or have more images to analyze!"
217
-
218
  output_html = "<div class='output'>"
219
  output_html += "<h3>Processing Results:</h3><ul>"
220
  for result in results:
221
- if 'Error' in result:
222
  output_html += f"<li class='error'>{result}</li>"
223
  else:
224
  output_html += f"<li class='success'>{result}</li>"
225
  output_html += "</ul>"
226
- output_html += f"<p><strong>Project Record Created:</strong> {project_name} (Total: {total_percent_complete}%)</p>"
227
- output_html += f"<p><strong>Grok's Analysis:</strong> {grok_summary}</p>"
228
- 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>"
 
 
 
 
 
229
  output_html += "</div>"
230
 
231
- return output_html, "Success", "", f"Max Confidence: {max([conf for _, conf in milestone_confidences], default=0)}", f"{total_percent_complete}%"
232
 
233
  except Exception as e:
 
234
  return f"<p style='color: red;'>Error: {str(e)}</p>", "Failure", "", "", "0%"
235
 
236
- # Gradio UI with updated new styling
237
  with gr.Blocks(css="""
238
  .gradio-container {
239
  background-color: #f9f9f9;
240
  font-family: 'Roboto', sans-serif;
 
241
  }
242
  .title {
243
  color: #34495e;
@@ -249,8 +294,10 @@ with gr.Blocks(css="""
249
  }
250
  .gradio-row {
251
  text-align: center;
 
 
252
  }
253
- .gradio-container .output {
254
  text-align: left;
255
  margin-top: 20px;
256
  padding: 30px;
@@ -261,32 +308,32 @@ with gr.Blocks(css="""
261
  margin-left: auto;
262
  margin-right: auto;
263
  }
264
- .gradio-container .output h3 {
265
  color: #2c3e50;
266
  font-size: 22px;
267
  font-weight: bold;
268
  margin-bottom: 20px;
269
  }
270
- .gradio-container .output ul {
271
  list-style-type: none;
272
  padding: 0;
273
  }
274
- .gradio-container .output li {
275
  padding: 14px;
276
  margin-bottom: 18px;
277
  border-radius: 10px;
278
  font-size: 16px;
279
  transition: background-color 0.3s ease;
280
  }
281
- .gradio-container .output li.success {
282
  background-color: #27ae60;
283
  color: white;
284
  }
285
- .gradio-container .output li.error {
286
  background-color: #e74c3c;
287
  color: white;
288
  }
289
- .gradio-container .button {
290
  display: block;
291
  margin: 25px auto;
292
  background-color: #3498db;
@@ -298,23 +345,24 @@ with gr.Blocks(css="""
298
  font-size: 18px;
299
  transition: background-color 0.3s ease;
300
  }
301
- .gradio-container .button:hover {
302
  background-color: #2980b9;
303
  }
304
- .gradio-container .input {
305
  text-align: center;
 
 
306
  }
307
  """) as demo:
308
- gr.Markdown("<h1 class='title'>Construction Progress Analyzer (Powered by Grok-like AI)</h1>")
309
-
310
  with gr.Row():
311
  project_type_input = gr.Dropdown(label="Project Type", choices=["House", "Apartment"], value="House")
312
  project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
313
-
314
  image_input = gr.File(
315
  file_count="multiple",
316
  file_types=["image"],
317
- label="Upload Construction Site Photos (JPG/PNG, ≤ 20MB each)"
318
  )
319
  submit_button = gr.Button("Process Images")
320
  output_html = gr.HTML(label="Result")
@@ -325,12 +373,5 @@ with gr.Blocks(css="""
325
  outputs=[output_html]
326
  )
327
 
328
- print("Launching Gradio interface at", datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%Y-%m-%d %H:%M:%S IST"))
329
- try:
330
- demo.launch(share=True, debug=True) # Block and enable debug mode
331
- print("Gradio interface launched successfully.")
332
- while True: # Keep the script alive
333
- time.sleep(10)
334
- except Exception as e:
335
- print(f"Failed to launch Gradio interface: {str(e)}")
336
- 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
 
108
  for y in range(height - 1):
109
  r, g, b = img_enhanced.getpixel((x, y))
110
  r_next, g_next, b_next = img_enhanced.getpixel((x + 1, y + 1))
111
+ if abs(r - r_next) > 50 or abs(g - g_next) > 50 or abs(b - b_next) > 50:
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()