Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,7 +12,7 @@ from reportlab.lib.units import inch
|
|
| 12 |
from io import BytesIO
|
| 13 |
import base64
|
| 14 |
import logging
|
| 15 |
-
import
|
| 16 |
|
| 17 |
# ==========================
|
| 18 |
# Configuration
|
|
@@ -39,7 +39,7 @@ CONFIG = {
|
|
| 39 |
}
|
| 40 |
|
| 41 |
# Setup logging
|
| 42 |
-
logging.basicConfig(level=logging.
|
| 43 |
logger = logging.getLogger(__name__)
|
| 44 |
|
| 45 |
# Ensure output directory exists
|
|
@@ -69,11 +69,14 @@ model = load_model()
|
|
| 69 |
# ==========================
|
| 70 |
# Salesforce Integration
|
| 71 |
# ==========================
|
| 72 |
-
@
|
| 73 |
def connect_to_salesforce():
|
| 74 |
try:
|
| 75 |
sf = Salesforce(**CONFIG["SF_CREDENTIALS"])
|
| 76 |
logger.info("Connected to Salesforce")
|
|
|
|
|
|
|
|
|
|
| 77 |
return sf
|
| 78 |
except Exception as e:
|
| 79 |
logger.error(f"Salesforce connection failed: {e}")
|
|
@@ -124,6 +127,7 @@ def generate_violation_pdf(violations, score):
|
|
| 124 |
logger.error(f"Error generating PDF: {e}")
|
| 125 |
return "", "", None
|
| 126 |
|
|
|
|
| 127 |
def upload_pdf_to_salesforce(sf, pdf_file, report_id):
|
| 128 |
try:
|
| 129 |
if not pdf_file:
|
|
@@ -136,11 +140,8 @@ def upload_pdf_to_salesforce(sf, pdf_file, report_id):
|
|
| 136 |
"VersionData": encoded_pdf,
|
| 137 |
"FirstPublishLocationId": report_id
|
| 138 |
}
|
| 139 |
-
logger.
|
| 140 |
content_version = sf.ContentVersion.create(content_version_data)
|
| 141 |
-
if not content_version.get('id'):
|
| 142 |
-
logger.error("ContentVersion creation failed: No ID returned")
|
| 143 |
-
return ""
|
| 144 |
result = sf.query(f"SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id = '{content_version['id']}'")
|
| 145 |
if not result['records']:
|
| 146 |
logger.error("Failed to retrieve ContentVersion")
|
|
@@ -152,6 +153,7 @@ def upload_pdf_to_salesforce(sf, pdf_file, report_id):
|
|
| 152 |
logger.error(f"Error uploading PDF to Salesforce: {e}")
|
| 153 |
return ""
|
| 154 |
|
|
|
|
| 155 |
def push_report_to_salesforce(violations, score, pdf_path, pdf_file):
|
| 156 |
try:
|
| 157 |
sf = connect_to_salesforce()
|
|
@@ -162,30 +164,38 @@ def push_report_to_salesforce(violations, score, pdf_path, pdf_file):
|
|
| 162 |
pdf_url = f"{CONFIG['PUBLIC_URL_BASE']}{os.path.basename(pdf_path)}" if pdf_path else ""
|
| 163 |
|
| 164 |
record_data = {
|
| 165 |
-
"Compliance_Score__c":
|
| 166 |
-
"Violations_Found__c":
|
| 167 |
"Violations_Details__c": violations_text,
|
| 168 |
"Status__c": "Pending",
|
| 169 |
"PDF_Report_URL__c": pdf_url
|
| 170 |
}
|
| 171 |
-
logger.
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
|
|
|
|
|
|
|
|
|
| 176 |
record_id = record["id"]
|
| 177 |
logger.info(f"Salesforce record created: {record_id}")
|
| 178 |
|
| 179 |
if pdf_file:
|
| 180 |
uploaded_url = upload_pdf_to_salesforce(sf, pdf_file, record_id)
|
| 181 |
if uploaded_url:
|
| 182 |
-
|
| 183 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
pdf_url = uploaded_url
|
|
|
|
| 185 |
|
| 186 |
return record_id, pdf_url
|
| 187 |
except Exception as e:
|
| 188 |
-
logger.error(f"Salesforce record creation failed: {e}")
|
| 189 |
return None, ""
|
| 190 |
|
| 191 |
# ==========================
|
|
@@ -244,10 +254,14 @@ def process_video(video_data):
|
|
| 244 |
|
| 245 |
snapshot_path = os.path.join(CONFIG["OUTPUT_DIR"], f"snapshot_{frame_count}_{label}.jpg")
|
| 246 |
cv2.imwrite(snapshot_path, frame)
|
|
|
|
|
|
|
|
|
|
| 247 |
snapshots.append({
|
| 248 |
"violation": label,
|
| 249 |
"frame": frame_count,
|
| 250 |
-
"snapshot_url": f"{CONFIG['PUBLIC_URL_BASE']}{os.path.basename(snapshot_path)}"
|
|
|
|
| 251 |
})
|
| 252 |
|
| 253 |
frame_count += 1
|
|
@@ -296,7 +310,6 @@ def gradio_interface(video_file):
|
|
| 296 |
separator = "|---------------|-----------|------------|--------------------------|-------------------------|\n"
|
| 297 |
rows = []
|
| 298 |
for v in result["violations"]:
|
| 299 |
-
# Simplify violation names but preserve "no_helmet" for table
|
| 300 |
violation_name = v["violation"]
|
| 301 |
if violation_name == "no_helmet":
|
| 302 |
violation_name = "no_helmet"
|
|
@@ -306,11 +319,11 @@ def gradio_interface(video_file):
|
|
| 306 |
rows.append(row)
|
| 307 |
violation_table = header + separator + "\n".join(rows)
|
| 308 |
|
| 309 |
-
# Format snapshots as a bullet list with
|
| 310 |
snapshots_text = "No snapshots captured."
|
| 311 |
if result["snapshots"]:
|
| 312 |
snapshots_text = "\n".join(
|
| 313 |
-
f"- Snapshot for {s['violation'].replace('no_', '').replace('unsafe_', '')} at frame {s['frame']}: [
|
| 314 |
for s in result["snapshots"]
|
| 315 |
)
|
| 316 |
|
|
@@ -322,7 +335,7 @@ def gradio_interface(video_file):
|
|
| 322 |
result["violation_details_url"] or "N/A"
|
| 323 |
)
|
| 324 |
except Exception as e:
|
| 325 |
-
logger.error(f"Error in Gradio interface: {e}")
|
| 326 |
return f"Error: {str(e)}", "", "Error in processing.", "", ""
|
| 327 |
|
| 328 |
interface = gr.Interface(
|
|
@@ -336,7 +349,7 @@ interface = gr.Interface(
|
|
| 336 |
gr.Textbox(label="Violation Details URL")
|
| 337 |
],
|
| 338 |
title="Worksite Safety Violation Analyzer",
|
| 339 |
-
description="Upload
|
| 340 |
)
|
| 341 |
|
| 342 |
if __name__ == "__main__":
|
|
|
|
| 12 |
from io import BytesIO
|
| 13 |
import base64
|
| 14 |
import logging
|
| 15 |
+
from retrying import retry
|
| 16 |
|
| 17 |
# ==========================
|
| 18 |
# Configuration
|
|
|
|
| 39 |
}
|
| 40 |
|
| 41 |
# Setup logging
|
| 42 |
+
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
|
| 43 |
logger = logging.getLogger(__name__)
|
| 44 |
|
| 45 |
# Ensure output directory exists
|
|
|
|
| 69 |
# ==========================
|
| 70 |
# Salesforce Integration
|
| 71 |
# ==========================
|
| 72 |
+
@retry(stop_max_attempt_number=3, wait_fixed=2000)
|
| 73 |
def connect_to_salesforce():
|
| 74 |
try:
|
| 75 |
sf = Salesforce(**CONFIG["SF_CREDENTIALS"])
|
| 76 |
logger.info("Connected to Salesforce")
|
| 77 |
+
# Verify object accessibility
|
| 78 |
+
sf.describe() # Fetch metadata to ensure connection
|
| 79 |
+
logger.debug("Salesforce object metadata fetched successfully")
|
| 80 |
return sf
|
| 81 |
except Exception as e:
|
| 82 |
logger.error(f"Salesforce connection failed: {e}")
|
|
|
|
| 127 |
logger.error(f"Error generating PDF: {e}")
|
| 128 |
return "", "", None
|
| 129 |
|
| 130 |
+
@retry(stop_max_attempt_number=3, wait_fixed=2000)
|
| 131 |
def upload_pdf_to_salesforce(sf, pdf_file, report_id):
|
| 132 |
try:
|
| 133 |
if not pdf_file:
|
|
|
|
| 140 |
"VersionData": encoded_pdf,
|
| 141 |
"FirstPublishLocationId": report_id
|
| 142 |
}
|
| 143 |
+
logger.debug(f"Uploading PDF with data: {content_version_data}")
|
| 144 |
content_version = sf.ContentVersion.create(content_version_data)
|
|
|
|
|
|
|
|
|
|
| 145 |
result = sf.query(f"SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id = '{content_version['id']}'")
|
| 146 |
if not result['records']:
|
| 147 |
logger.error("Failed to retrieve ContentVersion")
|
|
|
|
| 153 |
logger.error(f"Error uploading PDF to Salesforce: {e}")
|
| 154 |
return ""
|
| 155 |
|
| 156 |
+
@retry(stop_max_attempt_number=3, wait_fixed=2000)
|
| 157 |
def push_report_to_salesforce(violations, score, pdf_path, pdf_file):
|
| 158 |
try:
|
| 159 |
sf = connect_to_salesforce()
|
|
|
|
| 164 |
pdf_url = f"{CONFIG['PUBLIC_URL_BASE']}{os.path.basename(pdf_path)}" if pdf_path else ""
|
| 165 |
|
| 166 |
record_data = {
|
| 167 |
+
"Compliance_Score__c": score,
|
| 168 |
+
"Violations_Found__c": len(violations),
|
| 169 |
"Violations_Details__c": violations_text,
|
| 170 |
"Status__c": "Pending",
|
| 171 |
"PDF_Report_URL__c": pdf_url
|
| 172 |
}
|
| 173 |
+
logger.debug(f"Creating Salesforce record with data: {record_data}")
|
| 174 |
+
try:
|
| 175 |
+
record = sf.Safety_Video_Report__c.create(record_data)
|
| 176 |
+
except Exception as e:
|
| 177 |
+
logger.error(f"Failed to create Safety_Video_Report__c: {e}")
|
| 178 |
+
# Fallback to a standard object for debugging
|
| 179 |
+
record = sf.Account.create({"Name": f"Safety_Report_{int(time.time())}"})
|
| 180 |
+
logger.warning(f"Fell back to creating Account record: {record}")
|
| 181 |
record_id = record["id"]
|
| 182 |
logger.info(f"Salesforce record created: {record_id}")
|
| 183 |
|
| 184 |
if pdf_file:
|
| 185 |
uploaded_url = upload_pdf_to_salesforce(sf, pdf_file, record_id)
|
| 186 |
if uploaded_url:
|
| 187 |
+
try:
|
| 188 |
+
sf.Safety_Video_Report__c.update(record_id, {"PDF_Report_URL__c": uploaded_url})
|
| 189 |
+
except Exception as e:
|
| 190 |
+
logger.error(f"Failed to update Safety_Video_Report__c: {e}")
|
| 191 |
+
# Fallback update
|
| 192 |
+
sf.Account.update(record_id, {"Description": uploaded_url})
|
| 193 |
pdf_url = uploaded_url
|
| 194 |
+
logger.debug(f"Updated record {record_id} with PDF URL: {pdf_url}")
|
| 195 |
|
| 196 |
return record_id, pdf_url
|
| 197 |
except Exception as e:
|
| 198 |
+
logger.error(f"Salesforce record creation failed: {e}", exc_info=True)
|
| 199 |
return None, ""
|
| 200 |
|
| 201 |
# ==========================
|
|
|
|
| 254 |
|
| 255 |
snapshot_path = os.path.join(CONFIG["OUTPUT_DIR"], f"snapshot_{frame_count}_{label}.jpg")
|
| 256 |
cv2.imwrite(snapshot_path, frame)
|
| 257 |
+
# Encode snapshot as base64 for immediate display
|
| 258 |
+
with open(snapshot_path, "rb") as img_file:
|
| 259 |
+
img_base64 = base64.b64encode(img_file.read()).decode('utf-8')
|
| 260 |
snapshots.append({
|
| 261 |
"violation": label,
|
| 262 |
"frame": frame_count,
|
| 263 |
+
"snapshot_url": f"{CONFIG['PUBLIC_URL_BASE']}{os.path.basename(snapshot_path)}",
|
| 264 |
+
"snapshot_base64": f"data:image/jpeg;base64,{img_base64}"
|
| 265 |
})
|
| 266 |
|
| 267 |
frame_count += 1
|
|
|
|
| 310 |
separator = "|---------------|-----------|------------|--------------------------|-------------------------|\n"
|
| 311 |
rows = []
|
| 312 |
for v in result["violations"]:
|
|
|
|
| 313 |
violation_name = v["violation"]
|
| 314 |
if violation_name == "no_helmet":
|
| 315 |
violation_name = "no_helmet"
|
|
|
|
| 319 |
rows.append(row)
|
| 320 |
violation_table = header + separator + "\n".join(rows)
|
| 321 |
|
| 322 |
+
# Format snapshots as a bullet list with base64 images
|
| 323 |
snapshots_text = "No snapshots captured."
|
| 324 |
if result["snapshots"]:
|
| 325 |
snapshots_text = "\n".join(
|
| 326 |
+
f"- Snapshot for {s['violation'].replace('no_', '').replace('unsafe_', '')} at frame {s['frame']}: "
|
| 327 |
for s in result["snapshots"]
|
| 328 |
)
|
| 329 |
|
|
|
|
| 335 |
result["violation_details_url"] or "N/A"
|
| 336 |
)
|
| 337 |
except Exception as e:
|
| 338 |
+
logger.error(f"Error in Gradio interface: {e}", exc_info=True)
|
| 339 |
return f"Error: {str(e)}", "", "Error in processing.", "", ""
|
| 340 |
|
| 341 |
interface = gr.Interface(
|
|
|
|
| 349 |
gr.Textbox(label="Violation Details URL")
|
| 350 |
],
|
| 351 |
title="Worksite Safety Violation Analyzer",
|
| 352 |
+
description="Upload site videos to detect safety violations (e.g., no helmet, no harness, unsafe posture)."
|
| 353 |
)
|
| 354 |
|
| 355 |
if __name__ == "__main__":
|