Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,11 +4,10 @@ import os
|
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
from simple_salesforce import Salesforce
|
| 6 |
from datetime import datetime
|
|
|
|
| 7 |
import shutil
|
| 8 |
import base64
|
| 9 |
import pytz
|
| 10 |
-
import hashlib
|
| 11 |
-
import statistics
|
| 12 |
|
| 13 |
# Load environment variables
|
| 14 |
load_dotenv()
|
|
@@ -33,65 +32,28 @@ except Exception as e:
|
|
| 33 |
raise
|
| 34 |
|
| 35 |
# Valid milestones
|
| 36 |
-
VALID_MILESTONES = ["Planning", "Foundation", "Walls Erected", "
|
| 37 |
|
| 38 |
-
# Adjust the timezone to
|
| 39 |
local_timezone = pytz.timezone("Asia/Kolkata")
|
| 40 |
|
| 41 |
-
#
|
| 42 |
-
def analyze_image_with_hf(image_path):
|
| 43 |
-
img = Image.open(image_path)
|
| 44 |
-
detected_elements = []
|
| 45 |
-
|
| 46 |
-
width, height = img.size
|
| 47 |
-
total_pixels = width * height
|
| 48 |
-
img_data = list(img.convert('RGB').getdata())
|
| 49 |
-
gray_pixels = sum(1 for pixel in img_data if sum(pixel[:3]) / 3 < 128) / len(img_data)
|
| 50 |
-
color_variance = statistics.variance([sum(pixel[:3]) / 3 for pixel in img_data]) if len(img_data) > 1 else 0
|
| 51 |
-
|
| 52 |
-
# Adjusted logic to detect completed construction, overriding small size
|
| 53 |
-
if (color_variance > 2000 and gray_pixels < 0.4 and any(keyword in os.path.basename(image_path).lower() for keyword in ["window", "door", "roof"])) or \
|
| 54 |
-
(total_pixels > 800000 and gray_pixels < 0.4 and color_variance > 2000):
|
| 55 |
-
detected_elements.extend(["roof", "windows", "doors", "cladding", "finished"])
|
| 56 |
-
elif total_pixels < 200000 and color_variance < 800:
|
| 57 |
-
detected_elements.append("planning")
|
| 58 |
-
elif gray_pixels > 0.6 and total_pixels > 400000:
|
| 59 |
-
detected_elements.append("foundation")
|
| 60 |
-
elif gray_pixels > 0.35 and total_pixels > 800000 and color_variance > 1500:
|
| 61 |
-
detected_elements.append("walls")
|
| 62 |
-
elif gray_pixels < 0.25 or any(keyword in os.path.basename(image_path).lower() for keyword in ["interior", "plumbing", "electrical", "ceiling"]):
|
| 63 |
-
detected_elements.extend(["interior", "electrical_wiring", "plumbing_pipes"])
|
| 64 |
-
else:
|
| 65 |
-
detected_elements.append("walls")
|
| 66 |
-
|
| 67 |
-
return detected_elements
|
| 68 |
-
|
| 69 |
-
# Image processing and Salesforce upload with live preview
|
| 70 |
def process_image(images, project_name):
|
| 71 |
try:
|
| 72 |
if not images or len(images) == 0:
|
| 73 |
-
return "Error: Please upload at least one image to proceed.",
|
| 74 |
|
| 75 |
-
|
| 76 |
-
upload_statuses = []
|
| 77 |
milestones = []
|
| 78 |
-
progresses = []
|
| 79 |
-
|
| 80 |
for image in images:
|
| 81 |
-
img =
|
| 82 |
-
try:
|
| 83 |
-
img = Image.open(image)
|
| 84 |
-
except Exception as e:
|
| 85 |
-
if "cannot identify image file" in str(e):
|
| 86 |
-
return f"Error: Unsupported image format '{os.path.basename(image)}'. Please use JPG, JPEG, PNG, or convert to a supported format.", None, "Failure", "", "", 0
|
| 87 |
-
return f"Error: Failed to open image '{os.path.basename(image)}' - {str(e)}", None, "Failure", "", "", 0
|
| 88 |
-
|
| 89 |
image_size_mb = os.path.getsize(image) / (1024 * 1024)
|
| 90 |
if image_size_mb > 20:
|
| 91 |
-
return "Error: One or more images exceed 20MB.",
|
| 92 |
-
if not str(image).lower().endswith(('.jpg', '.jpeg', '.png'
|
| 93 |
-
return "Error: Only JPG
|
| 94 |
|
|
|
|
| 95 |
upload_dir = "public_uploads"
|
| 96 |
os.makedirs(upload_dir, exist_ok=True)
|
| 97 |
unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
@@ -99,197 +61,177 @@ def process_image(images, project_name):
|
|
| 99 |
saved_image_path = os.path.join(upload_dir, image_filename)
|
| 100 |
shutil.copy(image, saved_image_path)
|
| 101 |
|
|
|
|
| 102 |
with open(saved_image_path, 'rb') as image_file:
|
| 103 |
image_data = base64.b64encode(image_file.read()).decode('utf-8')
|
| 104 |
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
try:
|
| 107 |
-
content_version = {
|
| 108 |
-
'Title': image_filename,
|
| 109 |
-
'PathOnClient': saved_image_path,
|
| 110 |
-
'VersionData': image_data
|
| 111 |
-
}
|
| 112 |
content_version_result = sf.ContentVersion.create(content_version)
|
| 113 |
content_version_id = content_version_result['id']
|
| 114 |
file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
|
| 115 |
except Exception as e:
|
| 116 |
-
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
else:
|
| 119 |
-
|
| 120 |
|
| 121 |
-
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
"Initial project outline and objectives have been established.",
|
| 128 |
"Preliminary designs and architectural plans are drafted.",
|
| 129 |
"Stakeholder meetings and initial approvals are completed."
|
| 130 |
],
|
| 131 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
"Site preparation, including clearing and leveling, is finished.",
|
| 133 |
"Excavation for the foundation has been completed.",
|
| 134 |
"Concrete pouring for the foundation, including footings and slabs, is done.",
|
| 135 |
"Initial structural inspections for the foundation have been passed."
|
| 136 |
],
|
| 137 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
"The concrete framework, including columns and beams, is in place.",
|
| 139 |
"All structural walls have been erected and stabilized.",
|
| 140 |
"Temporary scaffolding and safety measures are installed for ongoing work.",
|
| 141 |
"Initial inspections for structural integrity have been completed."
|
| 142 |
],
|
| 143 |
-
"
|
| 144 |
-
"
|
| 145 |
-
"
|
| 146 |
-
"
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
"
|
|
|
|
| 152 |
"Interior work, including electrical, plumbing, and HVAC systems, is fully implemented.",
|
| 153 |
"Finishing touches, such as flooring, painting, and fixtures, are completed.",
|
| 154 |
"All phases of the project are finished, including final inspections and approvals."
|
|
|
|
|
|
|
|
|
|
| 155 |
]
|
| 156 |
}
|
|
|
|
| 157 |
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
final_milestone = "Planning"
|
| 161 |
-
percent_complete = 10
|
| 162 |
-
elif any(elem == "foundation" for elem in detected_elements):
|
| 163 |
-
completed_tasks.extend(all_tasks["Foundation"])
|
| 164 |
-
final_milestone = "Foundation"
|
| 165 |
-
percent_complete = 30
|
| 166 |
-
elif any(elem == "walls" for elem in detected_elements):
|
| 167 |
-
completed_tasks.extend(all_tasks["Walls Erected"])
|
| 168 |
-
final_milestone = "Walls Erected"
|
| 169 |
-
percent_complete = 50
|
| 170 |
-
elif any(elem in ["interior", "electrical_wiring", "plumbing_pipes"] for elem in detected_elements):
|
| 171 |
-
completed_tasks.extend(all_tasks["Interior Furnishing"])
|
| 172 |
-
final_milestone = "Interior Furnishing"
|
| 173 |
-
percent_complete = 80
|
| 174 |
-
elif all(elem in ["roof", "windows", "doors", "cladding", "finished"] for elem in detected_elements if elem in ["roof", "windows", "doors", "cladding", "finished"]):
|
| 175 |
-
completed_tasks.extend(all_tasks["Completed"])
|
| 176 |
-
final_milestone = "Completed"
|
| 177 |
-
percent_complete = 100
|
| 178 |
-
else:
|
| 179 |
-
completed_tasks.extend(all_tasks["Walls Erected"])
|
| 180 |
-
final_milestone = "Walls Erected"
|
| 181 |
-
percent_complete = 50
|
| 182 |
-
|
| 183 |
-
all_possible_tasks = [task for sublist in all_tasks.values() for task in sublist]
|
| 184 |
-
not_completed_tasks = [task for task in all_possible_tasks if task not in completed_tasks]
|
| 185 |
|
| 186 |
-
|
| 187 |
-
|
| 188 |
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
</div>
|
| 198 |
-
<div style="text-align: center;">
|
| 199 |
-
<h3 style="color: #34495e;">Completion</h3>
|
| 200 |
-
<progress value="{percent_complete}" max="100" style="width: 200px; height: 20px;"></progress>
|
| 201 |
-
<p>{percent_complete}%</p>
|
| 202 |
-
</div>
|
| 203 |
-
</div>
|
| 204 |
-
<h3 style="color: #2c3e50;">Milestone Timeline</h3>
|
| 205 |
-
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
| 206 |
-
<span style="color: #bdc3c7;">Planning</span>
|
| 207 |
-
<span style="color: #bdc3c7;">Foundation</span>
|
| 208 |
-
<span style="color: #bdc3c7;">Walls Erected</span>
|
| 209 |
-
<span style="color: #bdc3c7;">Interior Furnishing</span>
|
| 210 |
-
<span style="color: #2ecc71;">Completed</span>
|
| 211 |
-
</div>
|
| 212 |
-
<details style="margin-bottom: 20px;">
|
| 213 |
-
<summary style="color: #2c3e50; font-weight: bold;">Completed Tasks</summary>
|
| 214 |
-
<ul style="padding-left: 20px;">
|
| 215 |
-
{completed_html}
|
| 216 |
-
</ul>
|
| 217 |
-
</details>
|
| 218 |
-
<p style="color: green;">Project is fully completed as of 02:35 PM IST, June 20, 2025.</p>
|
| 219 |
</div>
|
| 220 |
-
""
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
<h2 style="color: #2c3e50; text-align: center;">Project Summary - {os.path.basename(image)}</h2>
|
| 225 |
-
<div style="display: flex; justify-content: space-around; margin-bottom: 20px;">
|
| 226 |
-
<div style="text-align: center;">
|
| 227 |
-
<h3 style="color: #34495e;">Detected Milestone</h3>
|
| 228 |
-
<p style="font-size: 18px; font-weight: bold;">{final_milestone}</p>
|
| 229 |
-
</div>
|
| 230 |
-
<div style="text-align: center;">
|
| 231 |
-
<h3 style="color: #34495e;">Completion</h3>
|
| 232 |
-
<progress value="{percent_complete}" max="100" style="width: 200px; height: 20px;"></progress>
|
| 233 |
-
<p>{percent_complete}%</p>
|
| 234 |
-
</div>
|
| 235 |
-
</div>
|
| 236 |
-
<h3 style="color: #2c3e50;">Milestone Timeline</h3>
|
| 237 |
-
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
| 238 |
-
<span style="color: {'#2ecc71' if final_milestone == 'Planning' else '#bdc3c7'};">Planning</span>
|
| 239 |
-
<span style="color: {'#2ecc71' if final_milestone == 'Foundation' else '#bdc3c7'};">Foundation</span>
|
| 240 |
-
<span style="color: {'#2ecc71' if final_milestone == 'Walls Erected' else '#bdc3c7'};">Walls Erected</span>
|
| 241 |
-
<span style="color: {'#2ecc71' if final_milestone == 'Interior Furnishing' else '#bdc3c7'};">Interior Furnishing</span>
|
| 242 |
-
<span style="color: {'#2ecc71' if final_milestone == 'Completed' else '#bdc3c7'};">Completed</span>
|
| 243 |
-
</div>
|
| 244 |
-
<details style="margin-bottom: 20px;">
|
| 245 |
-
<summary style="color: #2c3e50; font-weight: bold;">Completed Tasks</summary>
|
| 246 |
-
<ul style="padding-left: 20px;">
|
| 247 |
-
{completed_html}
|
| 248 |
-
</ul>
|
| 249 |
-
</details>
|
| 250 |
-
<details style="margin-bottom: 20px;">
|
| 251 |
-
<summary style="color: #2c3e50; font-weight: bold;">Not Completed Tasks</summary>
|
| 252 |
-
<ul style="padding-left: 20px;">
|
| 253 |
-
{not_completed_html}
|
| 254 |
-
</ul>
|
| 255 |
-
</details>
|
| 256 |
-
<p style="color: orange;">Construction is in progress at {final_milestone} stage as of 02:35 PM IST, June 20, 2025.</p>
|
| 257 |
</div>
|
| 258 |
-
|
| 259 |
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
|
|
|
|
|
|
|
|
|
| 264 |
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
-
|
| 268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
|
| 270 |
|
|
|
|
| 271 |
record = {
|
| 272 |
"Name__c": project_name,
|
| 273 |
-
"Current_Milestone__c":
|
| 274 |
-
"Completion_Percentage__c":
|
| 275 |
"Last_Updated_On__c": local_time,
|
| 276 |
-
"Upload_Status__c":
|
| 277 |
-
"Comments__c":
|
| 278 |
-
"Last_Updated_Image__c": file_url
|
| 279 |
}
|
| 280 |
|
| 281 |
try:
|
| 282 |
-
|
| 283 |
-
sf.Construction__c.create(record)
|
| 284 |
except Exception as e:
|
| 285 |
-
return f"Error: Failed to update Salesforce - {str(e)}",
|
| 286 |
|
| 287 |
-
return
|
| 288 |
|
| 289 |
except Exception as e:
|
| 290 |
-
return f"Error: {str(e)}",
|
| 291 |
|
| 292 |
-
# Gradio UI with enhanced styling
|
| 293 |
with gr.Blocks(css="""
|
| 294 |
.gradio-container {
|
| 295 |
background-color: #f0f4f8;
|
|
@@ -344,26 +286,18 @@ with gr.Blocks(css="""
|
|
| 344 |
gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
|
| 345 |
with gr.Row():
|
| 346 |
image_input = gr.Files(type="filepath", label="Upload Construction Site Photos (JPG/PNG, ≤ 20MB)")
|
| 347 |
-
image_preview = gr.Image(label="Image Preview", interactive=False)
|
| 348 |
project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
|
| 349 |
|
| 350 |
submit_button = gr.Button("Process Image")
|
| 351 |
-
output_html = gr.HTML(label="Result")
|
| 352 |
upload_status = gr.Textbox(label="Upload Status")
|
| 353 |
milestone = gr.Textbox(label="Detected Milestone")
|
| 354 |
-
confidence = gr.Textbox(label="Confidence Score") # Kept for compatibility, but unused
|
| 355 |
progress = gr.Textbox(label="Completion Percentage", interactive=False)
|
| 356 |
|
| 357 |
-
# Update preview on file upload
|
| 358 |
-
image_input.change(
|
| 359 |
-
fn=lambda x: Image.open(x[0]) if x else None,
|
| 360 |
-
inputs=[image_input],
|
| 361 |
-
outputs=[image_preview]
|
| 362 |
-
)
|
| 363 |
submit_button.click(
|
| 364 |
fn=process_image,
|
| 365 |
inputs=[image_input, project_name_input],
|
| 366 |
-
outputs=[output_html,
|
| 367 |
)
|
| 368 |
|
| 369 |
demo.launch(share=True)
|
|
|
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
from simple_salesforce import Salesforce
|
| 6 |
from datetime import datetime
|
| 7 |
+
import hashlib
|
| 8 |
import shutil
|
| 9 |
import base64
|
| 10 |
import pytz
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# Load environment variables
|
| 13 |
load_dotenv()
|
|
|
|
| 32 |
raise
|
| 33 |
|
| 34 |
# Valid milestones
|
| 35 |
+
VALID_MILESTONES = ["Planning", "Foundation", "Walls Erected", "Completed"]
|
| 36 |
|
| 37 |
+
# Adjust the timezone to your local timezone
|
| 38 |
local_timezone = pytz.timezone("Asia/Kolkata")
|
| 39 |
|
| 40 |
+
# Image processing and Salesforce upload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
def process_image(images, project_name):
|
| 42 |
try:
|
| 43 |
if not images or len(images) == 0:
|
| 44 |
+
return "Error: Please upload at least one image to proceed.", "Pending", "", 0
|
| 45 |
|
| 46 |
+
# Process each image
|
|
|
|
| 47 |
milestones = []
|
|
|
|
|
|
|
| 48 |
for image in images:
|
| 49 |
+
img = Image.open(image)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
image_size_mb = os.path.getsize(image) / (1024 * 1024)
|
| 51 |
if image_size_mb > 20:
|
| 52 |
+
return "Error: One or more images exceed 20MB.", "Failure", "", 0
|
| 53 |
+
if not str(image).lower().endswith(('.jpg', '.jpeg', '.png')):
|
| 54 |
+
return "Error: Only JPG/PNG images are supported.", "Failure", "", 0
|
| 55 |
|
| 56 |
+
# Save image to public folder temporarily before uploading to Salesforce
|
| 57 |
upload_dir = "public_uploads"
|
| 58 |
os.makedirs(upload_dir, exist_ok=True)
|
| 59 |
unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
|
|
| 61 |
saved_image_path = os.path.join(upload_dir, image_filename)
|
| 62 |
shutil.copy(image, saved_image_path)
|
| 63 |
|
| 64 |
+
# Convert image to base64 before uploading to Salesforce
|
| 65 |
with open(saved_image_path, 'rb') as image_file:
|
| 66 |
image_data = base64.b64encode(image_file.read()).decode('utf-8')
|
| 67 |
|
| 68 |
+
# Create the ContentVersion record in Salesforce
|
| 69 |
+
content_version = {
|
| 70 |
+
'Title': image_filename,
|
| 71 |
+
'PathOnClient': saved_image_path,
|
| 72 |
+
'VersionData': image_data
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
# Upload the file to Salesforce
|
| 76 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
content_version_result = sf.ContentVersion.create(content_version)
|
| 78 |
content_version_id = content_version_result['id']
|
| 79 |
file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
|
| 80 |
except Exception as e:
|
| 81 |
+
return f"Error: Failed to upload image to Salesforce - {str(e)}", "Failure", "", 0
|
| 82 |
+
|
| 83 |
+
# Mock AI model to detect milestone based on image content
|
| 84 |
+
img_bytes = img.tobytes()
|
| 85 |
+
img_hash = int(hashlib.sha256(img_bytes).hexdigest(), 16)
|
| 86 |
+
milestone_index = img_hash % len(VALID_MILESTONES)
|
| 87 |
+
milestone = VALID_MILESTONES[milestone_index]
|
| 88 |
+
|
| 89 |
+
# Adjust milestone detection based on internal/external features
|
| 90 |
+
if milestone == "Walls Erected" and any("interior" in img_text.lower() for img_text in [os.path.basename(image)] if img_text):
|
| 91 |
+
# Check for visible internal works like electrical/plumbing
|
| 92 |
+
if any(keyword in str(os.path.basename(image)).lower() for keyword in ["electrical", "plumbing", "wiring", "pipes"]):
|
| 93 |
+
milestone = "Completed" # Upgrade to Completed if internal works are detected
|
| 94 |
else:
|
| 95 |
+
milestone = "Walls Erected" # Retain if no internal works visible
|
| 96 |
|
| 97 |
+
milestones.append(milestone)
|
| 98 |
|
| 99 |
+
# Determine overall milestone (most advanced detected)
|
| 100 |
+
final_milestone = max(set(milestones), key=milestones.count) if milestones else "Planning"
|
| 101 |
+
milestone_completion_map = {
|
| 102 |
+
"Planning": 10,
|
| 103 |
+
"Foundation": 30,
|
| 104 |
+
"Walls Erected": 50,
|
| 105 |
+
"Completed": 100,
|
| 106 |
+
}
|
| 107 |
+
percent_complete = milestone_completion_map.get(final_milestone, 0)
|
| 108 |
+
|
| 109 |
+
# Detailed Completion Breakdown based on final milestone
|
| 110 |
+
completion_details = {
|
| 111 |
+
"Planning": {
|
| 112 |
+
"completed": [
|
| 113 |
"Initial project outline and objectives have been established.",
|
| 114 |
"Preliminary designs and architectural plans are drafted.",
|
| 115 |
"Stakeholder meetings and initial approvals are completed."
|
| 116 |
],
|
| 117 |
+
"not_completed": [
|
| 118 |
+
"Detailed construction plans and blueprints are pending finalization.",
|
| 119 |
+
"Permits and regulatory approvals are yet to be obtained.",
|
| 120 |
+
"Contractor selection and procurement processes are not yet complete."
|
| 121 |
+
]
|
| 122 |
+
},
|
| 123 |
+
"Foundation": {
|
| 124 |
+
"completed": [
|
| 125 |
"Site preparation, including clearing and leveling, is finished.",
|
| 126 |
"Excavation for the foundation has been completed.",
|
| 127 |
"Concrete pouring for the foundation, including footings and slabs, is done.",
|
| 128 |
"Initial structural inspections for the foundation have been passed."
|
| 129 |
],
|
| 130 |
+
"not_completed": [
|
| 131 |
+
"Plumbing and electrical groundwork installations are pending.",
|
| 132 |
+
"Backfilling and site grading around the foundation are not yet done.",
|
| 133 |
+
"Above-ground structural work, such as columns and walls, has not started."
|
| 134 |
+
]
|
| 135 |
+
},
|
| 136 |
+
"Walls Erected": {
|
| 137 |
+
"completed": [
|
| 138 |
"The concrete framework, including columns and beams, is in place.",
|
| 139 |
"All structural walls have been erected and stabilized.",
|
| 140 |
"Temporary scaffolding and safety measures are installed for ongoing work.",
|
| 141 |
"Initial inspections for structural integrity have been completed."
|
| 142 |
],
|
| 143 |
+
"not_completed": [
|
| 144 |
+
"Roofing installation and weatherproofing are pending.",
|
| 145 |
+
"Windows, doors, and exterior cladding are not yet installed.",
|
| 146 |
+
"Interior walls, electrical, and plumbing systems are still to be implemented."
|
| 147 |
+
]
|
| 148 |
+
},
|
| 149 |
+
"Completed": {
|
| 150 |
+
"completed": [
|
| 151 |
+
"The concrete framework, including columns, beams, and floor slabs, is fully constructed.",
|
| 152 |
+
"Exterior walls, windows, and cladding are installed, completing the building's facade.",
|
| 153 |
"Interior work, including electrical, plumbing, and HVAC systems, is fully implemented.",
|
| 154 |
"Finishing touches, such as flooring, painting, and fixtures, are completed.",
|
| 155 |
"All phases of the project are finished, including final inspections and approvals."
|
| 156 |
+
],
|
| 157 |
+
"not_completed": [
|
| 158 |
+
"There should be no more pending work as the project is fully completed."
|
| 159 |
]
|
| 160 |
}
|
| 161 |
+
}
|
| 162 |
|
| 163 |
+
completed_tasks = completion_details[final_milestone]["completed"]
|
| 164 |
+
not_completed_tasks = completion_details[final_milestone]["not_completed"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
+
completed_html = "".join([f'<li style="color: green;">✔ {task}</li>' for task in completed_tasks])
|
| 167 |
+
not_completed_html = "".join([f'<li style="color: red;">✘ {task}</li>' for task in not_completed_tasks])
|
| 168 |
|
| 169 |
+
# Create HTML for the output with collapsible sections and progress bar
|
| 170 |
+
result_html = f"""
|
| 171 |
+
<div style="font-family: Arial, sans-serif; padding: 20px; background-color: #f9f9f9; border-radius: 10px;">
|
| 172 |
+
<h2 style="color: #2c3e50; text-align: center;">Project Summary</h2>
|
| 173 |
+
<div style="display: flex; justify-content: space-around; margin-bottom: 20px;">
|
| 174 |
+
<div style="text-align: center;">
|
| 175 |
+
<h3 style="color: #34495e;">Detected Milestone</h3>
|
| 176 |
+
<p style="font-size: 18px; font-weight: bold;">{final_milestone}</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
</div>
|
| 178 |
+
<div style="text-align: center;">
|
| 179 |
+
<h3 style="color: #34495e;">Completion</h3>
|
| 180 |
+
<progress value="{percent_complete}" max="100" style="width: 200px; height: 20px;"></progress>
|
| 181 |
+
<p>{percent_complete}%</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
</div>
|
| 183 |
+
</div>
|
| 184 |
|
| 185 |
+
<h3 style="color: #2c3e50;">Milestone Timeline</h3>
|
| 186 |
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
| 187 |
+
<span style="color: {'#2ecc71' if final_milestone == 'Planning' else '#bdc3c7'};">Planning</span>
|
| 188 |
+
<span style="color: {'#2ecc71' if final_milestone == 'Foundation' else '#bdc3c7'};">Foundation</span>
|
| 189 |
+
<span style="color: {'#2ecc71' if final_milestone == 'Walls Erected' else '#bdc3c7'};">Walls Erected</span>
|
| 190 |
+
<span style="color: {'#2ecc71' if final_milestone == 'Completed' else '#bdc3c7'};">Completed</span>
|
| 191 |
+
</div>
|
| 192 |
|
| 193 |
+
<details style="margin-bottom: 20px;">
|
| 194 |
+
<summary style="color: #2c3e50; font-weight: bold;">Completed Tasks</summary>
|
| 195 |
+
<ul style="padding-left: 20px;">
|
| 196 |
+
{completed_html}
|
| 197 |
+
</ul>
|
| 198 |
+
</details>
|
| 199 |
|
| 200 |
+
<details style="margin-bottom: 20px;">
|
| 201 |
+
<summary style="color: #2c3e50; font-weight: bold;">Not Completed Tasks</summary>
|
| 202 |
+
<ul style="padding-left: 20px;">
|
| 203 |
+
{not_completed_html}
|
| 204 |
+
</ul>
|
| 205 |
+
</details>
|
| 206 |
+
</div>
|
| 207 |
+
"""
|
| 208 |
+
|
| 209 |
+
# Adjust the current time to local timezone with proper ISO 8601 format
|
| 210 |
+
now = datetime.now(local_timezone)
|
| 211 |
local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
|
| 212 |
|
| 213 |
+
# Create the Salesforce record (removing confidence score)
|
| 214 |
record = {
|
| 215 |
"Name__c": project_name,
|
| 216 |
+
"Current_Milestone__c": final_milestone,
|
| 217 |
+
"Completion_Percentage__c": percent_complete,
|
| 218 |
"Last_Updated_On__c": local_time,
|
| 219 |
+
"Upload_Status__c": "Success",
|
| 220 |
+
"Comments__c": f"{final_milestone}",
|
| 221 |
+
"Last_Updated_Image__c": file_url
|
| 222 |
}
|
| 223 |
|
| 224 |
try:
|
| 225 |
+
sf.Construction__c.create(record)
|
|
|
|
| 226 |
except Exception as e:
|
| 227 |
+
return f"Error: Failed to update Salesforce - {str(e)}", "Failure", "", 0
|
| 228 |
|
| 229 |
+
return result_html, "Success", final_milestone, f"{percent_complete}%"
|
| 230 |
|
| 231 |
except Exception as e:
|
| 232 |
+
return f"Error: {str(e)}", "Failure", "", "0%"
|
| 233 |
|
| 234 |
+
# Gradio UI with enhanced styling
|
| 235 |
with gr.Blocks(css="""
|
| 236 |
.gradio-container {
|
| 237 |
background-color: #f0f4f8;
|
|
|
|
| 286 |
gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
|
| 287 |
with gr.Row():
|
| 288 |
image_input = gr.Files(type="filepath", label="Upload Construction Site Photos (JPG/PNG, ≤ 20MB)")
|
|
|
|
| 289 |
project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
|
| 290 |
|
| 291 |
submit_button = gr.Button("Process Image")
|
| 292 |
+
output_html = gr.HTML(label="Result") # Changed to HTML for richer output
|
| 293 |
upload_status = gr.Textbox(label="Upload Status")
|
| 294 |
milestone = gr.Textbox(label="Detected Milestone")
|
|
|
|
| 295 |
progress = gr.Textbox(label="Completion Percentage", interactive=False)
|
| 296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
submit_button.click(
|
| 298 |
fn=process_image,
|
| 299 |
inputs=[image_input, project_name_input],
|
| 300 |
+
outputs=[output_html, upload_status, milestone, progress]
|
| 301 |
)
|
| 302 |
|
| 303 |
demo.launch(share=True)
|