PrashanthB461 commited on
Commit
f225f91
·
verified ·
1 Parent(s): 8223186

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -149
app.py CHANGED
@@ -11,84 +11,84 @@ from reportlab.pdfgen import canvas
11
  from reportlab.lib.units import inch
12
  from io import BytesIO
13
  import base64
 
14
 
15
  # ==========================
16
  # Configuration
17
  # ==========================
18
- DEFAULT_MODEL_PATH = "models/yolov8_safety.pt"
19
- FALLBACK_MODEL = "yolov8n.pt"
20
- MODEL_PATH = os.getenv("SAFETY_MODEL_PATH", DEFAULT_MODEL_PATH)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- # Use static/output for publicly accessible files
23
- STATIC_OUTPUT_DIR = "static/output"
24
- os.makedirs(STATIC_OUTPUT_DIR, exist_ok=True)
25
 
26
- VIOLATION_LABELS = {
27
- 0: "no_helmet",
28
- 1: "no_harness",
29
- 2: "unsafe_posture",
30
- 3: "unsafe_zone"
31
- }
32
 
33
  # ==========================
34
  # Device Setup
35
  # ==========================
36
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
37
- print(f"Using device: {device}")
38
 
39
  # ==========================
40
- # Load Model
41
  # ==========================
42
- try:
43
- selected_model = MODEL_PATH if os.path.isfile(MODEL_PATH) else FALLBACK_MODEL
44
- model = YOLO(selected_model)
45
- print(f"✅ Model loaded: {selected_model}")
46
- except Exception as e:
47
- print(f"❌ Failed to load model: {e}")
48
- raise
 
 
 
 
49
 
50
  # ==========================
51
- # Salesforce connection
52
  # ==========================
53
  def connect_to_salesforce():
54
  try:
55
- sf = Salesforce(
56
- username="prashanth1ai@safety.com",
57
- password="SaiPrash461",
58
- security_token="AP4AQnPoidIKPvSvNEfAHyoK",
59
- domain="login"
60
- )
61
- print("✅ Connected to Salesforce")
62
  return sf
63
  except Exception as e:
64
- print(f"Salesforce connection failed: {e}")
65
  raise
66
 
67
- def violations_to_text(violations):
68
- lines = []
69
- for v in violations:
70
- line = f"{v['violation']} at {v['timestamp']:.2f}s (Confidence: {v['confidence']})"
71
- lines.append(line)
72
- return "\n".join(lines)
73
-
74
  def generate_violation_pdf(violations, score):
75
  try:
76
- # Ensure the directory is writable
77
- if not os.access(STATIC_OUTPUT_DIR, os.W_OK):
78
- raise PermissionError(f"Cannot write to directory: {STATIC_OUTPUT_DIR}")
79
-
80
  pdf_filename = f"violations_{int(time.time())}.pdf"
81
- pdf_path = os.path.join(STATIC_OUTPUT_DIR, pdf_filename)
82
-
83
- # Generate PDF
84
  pdf_file = BytesIO()
85
  c = canvas.Canvas(pdf_file, pagesize=letter)
86
  c.setFont("Helvetica", 12)
87
  c.drawString(1 * inch, 10 * inch, "Worksite Safety Violation Report")
88
  c.setFont("Helvetica", 10)
89
- y_position = 9.5 * inch
90
 
91
- # Add report details
92
  report_data = {
93
  "Compliance Score": f"{score}%",
94
  "Violations Found": len(violations),
@@ -98,15 +98,14 @@ def generate_violation_pdf(violations, score):
98
  c.drawString(1 * inch, y_position, f"{key}: {value}")
99
  y_position -= 0.3 * inch
100
 
101
- # Add violation details
102
  y_position -= 0.3 * inch
103
  c.drawString(1 * inch, y_position, "Violation Details:")
104
  y_position -= 0.3 * inch
105
  for v in violations:
106
- violation_text = f"{v['violation']} at {v['timestamp']:.2f}s (Confidence: {v['confidence']})"
107
- c.drawString(1 * inch, y_position, violation_text)
108
  y_position -= 0.3 * inch
109
- if y_position < 1 * inch: # Handle page overflow
110
  c.showPage()
111
  c.setFont("Helvetica", 10)
112
  y_position = 10 * inch
@@ -115,142 +114,115 @@ def generate_violation_pdf(violations, score):
115
  c.save()
116
  pdf_file.seek(0)
117
 
118
- # Save PDF to disk
119
  with open(pdf_path, "wb") as f:
120
  f.write(pdf_file.getvalue())
121
-
122
- print(f"PDF generated at {pdf_path}")
123
- if not os.path.exists(pdf_path):
124
- raise FileNotFoundError(f"PDF was not created at {pdf_path}")
125
-
126
- public_url = f"https://huggingface.co/spaces/PrashanthB461/AI_Safety_Demo1/resolve/main/static/output/{pdf_filename}"
127
- print(f"✅ PDF URL: {public_url}")
128
  return pdf_path, public_url, pdf_file
129
  except Exception as e:
130
- print(f"Error generating PDF: {e}")
131
  return "", "", None
132
 
133
- def upload_pdf_to_salesforce(pdf_file: BytesIO, report_id: str):
134
  try:
135
- sf = connect_to_salesforce()
136
  if not pdf_file:
137
- print("No PDF file provided for upload")
138
  return ""
139
-
140
- encoded_pdf_data = base64.b64encode(pdf_file.getvalue()).decode('utf-8')
141
  content_version_data = {
142
  "Title": f"Safety_Violation_Report_{int(time.time())}",
143
  "PathOnClient": f"safety_violation_{int(time.time())}.pdf",
144
- "VersionData": encoded_pdf_data,
145
  "FirstPublishLocationId": report_id
146
  }
147
-
148
  content_version = sf.ContentVersion.create(content_version_data)
149
- content_version_id = content_version["id"]
150
-
151
- result = sf.query(f"SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id = '{content_version_id}'")
152
  if not result['records']:
153
- print("Failed to retrieve ContentVersion")
154
  return ""
155
-
156
- file_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{content_version_id}"
157
- print(f"✅ PDF uploaded to Salesforce, URL: {file_url}")
158
  return file_url
159
  except Exception as e:
160
- print(f"Error uploading PDF to Salesforce: {e}")
161
  return ""
162
 
163
- def push_report_to_salesforce(score, violations, pdf_path, pdf_file):
164
  try:
165
  sf = connect_to_salesforce()
166
- violations_count = len(violations)
167
- violations_details = violations_to_text(violations)
168
-
169
- PUBLIC_BASE_URL = "https://huggingface.co/spaces/PrashanthB461/AI_Safety_Demo1/resolve/main/static/output/"
170
- pdf_url = f"{PUBLIC_BASE_URL}{os.path.basename(pdf_path)}"
171
 
172
  record_data = {
173
  "Compliance_Score__c": score,
174
- "Violations_Found__c": violations_count,
175
- "Violations_Details__c": violations_details,
176
  "Status__c": "Pending",
177
  "PDF_Report_URL__c": pdf_url
178
  }
179
-
180
- print(f"Attempting to create Salesforce record with data: {record_data}")
181
  record = sf.Safety_Video_Report__c.create(record_data)
182
- record_id = record.get('id')
183
- print(f"Salesforce record created with Id: {record_id}")
184
 
185
- # Upload PDF to Salesforce
186
  if pdf_file:
187
- uploaded_url = upload_pdf_to_salesforce(pdf_file, record_id)
188
  if uploaded_url:
189
- sf.Safety_Video_Report__c.update(
190
- record_id,
191
- {"PDF_Report_URL__c": uploaded_url}
192
- )
193
  pdf_url = uploaded_url
194
 
195
  return record_id, pdf_url
196
  except Exception as e:
197
- print(f"Salesforce record creation failed: {e}")
198
  return None, ""
199
 
200
  # ==========================
201
  # Safety Score Calculation
202
  # ==========================
203
  def calculate_safety_score(violations):
204
- base_score = 100
205
  penalties = {
206
  "no_helmet": 25,
207
  "no_harness": 30,
208
  "unsafe_posture": 20,
209
  "unsafe_zone": 25
210
  }
211
- for v in violations:
212
- base_score -= penalties.get(v["violation"], 0)
213
- return max(base_score, 0)
214
 
215
  # ==========================
216
  # Video Processing
217
  # ==========================
218
- def process_video(video_data, frame_skip=5, max_frames=100):
219
  try:
220
- print("Processing video data...")
221
- video_path = os.path.join(STATIC_OUTPUT_DIR, f"temp_{int(time.time())}.mp4")
222
  with open(video_path, "wb") as f:
223
  f.write(video_data)
224
- print(f"Video saved to {video_path}")
225
 
226
  video = cv2.VideoCapture(video_path)
227
  if not video.isOpened():
228
- raise ValueError("Could not open video file.")
229
 
230
- frame_count = 0
231
- violations = []
232
- snapshots = []
233
- processed_frame_count = 0
234
  start_time = time.time()
235
 
236
- while True:
237
  ret, frame = video.read()
238
  if not ret:
239
  break
240
-
241
- if frame_count % frame_skip != 0:
242
  frame_count += 1
243
  continue
244
 
245
  results = model(frame, device=device)
246
-
247
  for result in results:
248
  for box in result.boxes:
249
- cls = int(box.cls)
250
- conf = float(box.conf)
251
  xywh = box.xywh.cpu().numpy()[0]
252
-
253
- label = VIOLATION_LABELS.get(cls, f"class_{cls}")
254
  violation = {
255
  "frame": frame_count,
256
  "violation": label,
@@ -260,36 +232,25 @@ def process_video(video_data, frame_skip=5, max_frames=100):
260
  }
261
  violations.append(violation)
262
 
263
- snapshot_path = os.path.join(STATIC_OUTPUT_DIR, f"snapshot_{frame_count}_{label}.jpg")
264
  cv2.imwrite(snapshot_path, frame)
265
  snapshots.append({
266
  "violation": label,
267
  "frame": frame_count,
268
- "snapshot_url": f"https://huggingface.co/spaces/PrashanthB461/AI_Safety_Demo1/resolve/main/static/output/{os.path.basename(snapshot_path)}"
269
  })
270
 
271
  frame_count += 1
272
- processed_frame_count += 1
273
-
274
- if processed_frame_count >= max_frames:
275
- break
276
-
277
- if time.time() - start_time > 30:
278
- print("⏰ Exceeded 30 seconds of processing time.")
279
  break
280
 
281
  video.release()
282
  os.remove(video_path)
283
-
284
  score = calculate_safety_score(violations)
285
  pdf_path, pdf_url, pdf_file = generate_violation_pdf(violations, score)
286
-
287
- if pdf_path:
288
- report_id, final_pdf_url = push_report_to_salesforce(score, violations, pdf_path, pdf_file)
289
- print(f"✅ Salesforce record created with Id: {report_id}")
290
- else:
291
- report_id, final_pdf_url = None, ""
292
- print("❌ No PDF generated, skipping Salesforce record creation")
293
 
294
  return {
295
  "violations": violations,
@@ -297,43 +258,42 @@ def process_video(video_data, frame_skip=5, max_frames=100):
297
  "score": score,
298
  "salesforce_record_id": report_id,
299
  "violation_details_url": final_pdf_url,
300
- "violation_details_content": violations_to_text(violations) if violations else "No violations detected."
 
 
 
301
  }
302
-
303
  except Exception as e:
304
- print(f"Error processing video: {e}")
305
  return {
306
  "violations": [],
307
  "snapshots": [],
308
  "score": 0,
309
  "salesforce_record_id": None,
310
  "violation_details_url": "",
311
- "violation_details_content": "Error: Could not process video.",
312
- "error": str(e)
313
  }
314
 
315
  # ==========================
316
  # Gradio Interface
317
  # ==========================
318
  def gradio_interface(video_file):
 
 
319
  try:
320
- if not video_file:
321
- return {"error": "Please upload a video file."}, "", [], "", "", "No file uploaded."
322
-
323
  with open(video_file, "rb") as f:
324
  video_data = f.read()
325
-
326
  result = process_video(video_data)
327
  return (
328
- result.get("violations", []),
329
- f"Safety Score: {result.get('score', 0)}%",
330
- result.get("snapshots", []),
331
- f"Salesforce Record ID: {result.get('salesforce_record_id', '')}",
332
- f"Violation Details URL: {result.get('violation_details_url', '')}",
333
- result.get("violation_details_content", "No content available.")
334
  )
335
  except Exception as e:
336
- print(f"Error in gradio_interface: {e}")
337
  return {"error": str(e)}, "", [], "", "", "Error in processing."
338
 
339
  interface = gr.Interface(
@@ -345,12 +305,12 @@ interface = gr.Interface(
345
  gr.JSON(label="Snapshots"),
346
  gr.Textbox(label="Salesforce Record ID"),
347
  gr.Textbox(label="Violation Details URL"),
348
- gr.Textbox(label="Violation Details Content (Preview)")
349
  ],
350
  title="Worksite Safety Violation Analyzer",
351
  description="Upload short site videos to detect safety violations (e.g., no helmet, no harness, unsafe posture)."
352
  )
353
 
354
  if __name__ == "__main__":
355
- print("🚀 Launching Safety Analyzer App...")
356
  interface.launch()
 
11
  from reportlab.lib.units import inch
12
  from io import BytesIO
13
  import base64
14
+ import logging
15
 
16
  # ==========================
17
  # Configuration
18
  # ==========================
19
+ CONFIG = {
20
+ "MODEL_PATH": os.getenv("SAFETY_MODEL_PATH", "models/yolov8_safety.pt"),
21
+ "FALLBACK_MODEL": "yolov8n.pt",
22
+ "OUTPUT_DIR": "static/output",
23
+ "VIOLATION_LABELS": {
24
+ 0: "no_helmet",
25
+ 1: "no_harness",
26
+ 2: "unsafe_posture",
27
+ 3: "unsafe_zone"
28
+ },
29
+ "SF_CREDENTIALS": {
30
+ "username": "prashanth1ai@safety.com",
31
+ "password": "SaiPrash461",
32
+ "security_token": "AP4AQnPoidIKPvSvNEfAHyoK",
33
+ "domain": "login"
34
+ },
35
+ "PUBLIC_URL_BASE": "https://huggingface.co/spaces/PrashanthB461/AI_Safety_Demo1/resolve/main/static/output/",
36
+ "FRAME_SKIP": 5,
37
+ "MAX_FRAMES": 100,
38
+ "MAX_PROCESSING_TIME": 30
39
+ }
40
 
41
+ # Setup logging
42
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
43
+ logger = logging.getLogger(__name__)
44
 
45
+ # Ensure output directory exists
46
+ os.makedirs(CONFIG["OUTPUT_DIR"], exist_ok=True)
 
 
 
 
47
 
48
  # ==========================
49
  # Device Setup
50
  # ==========================
51
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
52
+ logger.info(f"Using device: {device}")
53
 
54
  # ==========================
55
+ # Model Loading
56
  # ==========================
57
+ def load_model():
58
+ model_path = CONFIG["MODEL_PATH"] if os.path.isfile(CONFIG["MODEL_PATH"]) else CONFIG["FALLBACK_MODEL"]
59
+ try:
60
+ model = YOLO(model_path).to(device)
61
+ logger.info(f"Model loaded: {model_path}")
62
+ return model
63
+ except Exception as e:
64
+ logger.error(f"Failed to load model: {e}")
65
+ raise
66
+
67
+ model = load_model()
68
 
69
  # ==========================
70
+ # Salesforce Integration
71
  # ==========================
72
  def connect_to_salesforce():
73
  try:
74
+ sf = Salesforce(**CONFIG["SF_CREDENTIALS"])
75
+ logger.info("Connected to Salesforce")
 
 
 
 
 
76
  return sf
77
  except Exception as e:
78
+ logger.error(f"Salesforce connection failed: {e}")
79
  raise
80
 
 
 
 
 
 
 
 
81
  def generate_violation_pdf(violations, score):
82
  try:
 
 
 
 
83
  pdf_filename = f"violations_{int(time.time())}.pdf"
84
+ pdf_path = os.path.join(CONFIG["OUTPUT_DIR"], pdf_filename)
 
 
85
  pdf_file = BytesIO()
86
  c = canvas.Canvas(pdf_file, pagesize=letter)
87
  c.setFont("Helvetica", 12)
88
  c.drawString(1 * inch, 10 * inch, "Worksite Safety Violation Report")
89
  c.setFont("Helvetica", 10)
 
90
 
91
+ y_position = 9.5 * inch
92
  report_data = {
93
  "Compliance Score": f"{score}%",
94
  "Violations Found": len(violations),
 
98
  c.drawString(1 * inch, y_position, f"{key}: {value}")
99
  y_position -= 0.3 * inch
100
 
 
101
  y_position -= 0.3 * inch
102
  c.drawString(1 * inch, y_position, "Violation Details:")
103
  y_position -= 0.3 * inch
104
  for v in violations:
105
+ text = f"{v['violation']} at {v['timestamp']:.2f}s (Confidence: {v['confidence']})"
106
+ c.drawString(1 * inch, y_position, text)
107
  y_position -= 0.3 * inch
108
+ if y_position < 1 * inch:
109
  c.showPage()
110
  c.setFont("Helvetica", 10)
111
  y_position = 10 * inch
 
114
  c.save()
115
  pdf_file.seek(0)
116
 
 
117
  with open(pdf_path, "wb") as f:
118
  f.write(pdf_file.getvalue())
119
+ public_url = f"{CONFIG['PUBLIC_URL_BASE']}{pdf_filename}"
120
+ logger.info(f"PDF generated: {public_url}")
 
 
 
 
 
121
  return pdf_path, public_url, pdf_file
122
  except Exception as e:
123
+ logger.error(f"Error generating PDF: {e}")
124
  return "", "", None
125
 
126
+ def upload_pdf_to_salesforce(sf, pdf_file, report_id):
127
  try:
 
128
  if not pdf_file:
129
+ logger.error("No PDF file provided for upload")
130
  return ""
131
+ encoded_pdf = base64.b64encode(pdf_file.getvalue()).decode('utf-8')
 
132
  content_version_data = {
133
  "Title": f"Safety_Violation_Report_{int(time.time())}",
134
  "PathOnClient": f"safety_violation_{int(time.time())}.pdf",
135
+ "VersionData": encoded_pdf,
136
  "FirstPublishLocationId": report_id
137
  }
 
138
  content_version = sf.ContentVersion.create(content_version_data)
139
+ result = sf.query(f"SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id = '{content_version['id']}'")
 
 
140
  if not result['records']:
141
+ logger.error("Failed to retrieve ContentVersion")
142
  return ""
143
+ file_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{content_version['id']}"
144
+ logger.info(f"PDF uploaded to Salesforce: {file_url}")
 
145
  return file_url
146
  except Exception as e:
147
+ logger.error(f"Error uploading PDF to Salesforce: {e}")
148
  return ""
149
 
150
+ def push_report_to_salesforce(violations, score, pdf_path, pdf_file):
151
  try:
152
  sf = connect_to_salesforce()
153
+ violations_text = "\n".join(
154
+ f"{v['violation']} at {v['timestamp']:.2f}s (Confidence: {v['confidence']})"
155
+ for v in violations
156
+ ) or "No violations detected."
157
+ pdf_url = f"{CONFIG['PUBLIC_URL_BASE']}{os.path.basename(pdf_path)}" if pdf_path else ""
158
 
159
  record_data = {
160
  "Compliance_Score__c": score,
161
+ "Violations_Found__c": len(violations),
162
+ "Violations_Details__c": violations_text,
163
  "Status__c": "Pending",
164
  "PDF_Report_URL__c": pdf_url
165
  }
 
 
166
  record = sf.Safety_Video_Report__c.create(record_data)
167
+ record_id = record["id"]
168
+ logger.info(f"Salesforce record created: {record_id}")
169
 
 
170
  if pdf_file:
171
+ uploaded_url = upload_pdf_to_salesforce(sf, pdf_file, record_id)
172
  if uploaded_url:
173
+ sf.Safety_Video_Report__c.update(record_id, {"PDF_Report_URL__c": uploaded_url})
 
 
 
174
  pdf_url = uploaded_url
175
 
176
  return record_id, pdf_url
177
  except Exception as e:
178
+ logger.error(f"Salesforce record creation failed: {e}")
179
  return None, ""
180
 
181
  # ==========================
182
  # Safety Score Calculation
183
  # ==========================
184
  def calculate_safety_score(violations):
 
185
  penalties = {
186
  "no_helmet": 25,
187
  "no_harness": 30,
188
  "unsafe_posture": 20,
189
  "unsafe_zone": 25
190
  }
191
+ score = 100 - sum(penalties.get(v["violation"], 0) for v in violations)
192
+ return max(score, 0)
 
193
 
194
  # ==========================
195
  # Video Processing
196
  # ==========================
197
+ def process_video(video_data):
198
  try:
199
+ video_path = os.path.join(CONFIG["OUTPUT_DIR"], f"temp_{int(time.time())}.mp4")
 
200
  with open(video_path, "wb") as f:
201
  f.write(video_data)
202
+ logger.info(f"Video saved: {video_path}")
203
 
204
  video = cv2.VideoCapture(video_path)
205
  if not video.isOpened():
206
+ raise ValueError("Could not open video file")
207
 
208
+ violations, snapshots = [], []
209
+ frame_count, processed_frames = 0, 0
 
 
210
  start_time = time.time()
211
 
212
+ while processed_frames < CONFIG["MAX_FRAMES"]:
213
  ret, frame = video.read()
214
  if not ret:
215
  break
216
+ if frame_count % CONFIG["FRAME_SKIP"] != 0:
 
217
  frame_count += 1
218
  continue
219
 
220
  results = model(frame, device=device)
 
221
  for result in results:
222
  for box in result.boxes:
223
+ cls, conf = int(box.cls), float(box.conf)
 
224
  xywh = box.xywh.cpu().numpy()[0]
225
+ label = CONFIG["VIOLATION_LABELS"].get(cls, f"class_{cls}")
 
226
  violation = {
227
  "frame": frame_count,
228
  "violation": label,
 
232
  }
233
  violations.append(violation)
234
 
235
+ snapshot_path = os.path.join(CONFIG["OUTPUT_DIR"], f"snapshot_{frame_count}_{label}.jpg")
236
  cv2.imwrite(snapshot_path, frame)
237
  snapshots.append({
238
  "violation": label,
239
  "frame": frame_count,
240
+ "snapshot_url": f"{CONFIG['PUBLIC_URL_BASE']}{os.path.basename(snapshot_path)}"
241
  })
242
 
243
  frame_count += 1
244
+ processed_frames += 1
245
+ if time.time() - start_time > CONFIG["MAX_PROCESSING_TIME"]:
246
+ logger.warning("Processing time limit exceeded")
 
 
 
 
247
  break
248
 
249
  video.release()
250
  os.remove(video_path)
 
251
  score = calculate_safety_score(violations)
252
  pdf_path, pdf_url, pdf_file = generate_violation_pdf(violations, score)
253
+ report_id, final_pdf_url = push_report_to_salesforce(violations, score, pdf_path, pdf_file)
 
 
 
 
 
 
254
 
255
  return {
256
  "violations": violations,
 
258
  "score": score,
259
  "salesforce_record_id": report_id,
260
  "violation_details_url": final_pdf_url,
261
+ "violation_details_content": "\n".join(
262
+ f"{v['violation']} at {v['timestamp']:.2f}s (Confidence: {v['confidence']})"
263
+ for v in violations
264
+ ) or "No violations detected."
265
  }
 
266
  except Exception as e:
267
+ logger.error(f"Error processing video: {e}")
268
  return {
269
  "violations": [],
270
  "snapshots": [],
271
  "score": 0,
272
  "salesforce_record_id": None,
273
  "violation_details_url": "",
274
+ "violation_details_content": f"Error: {str(e)}"
 
275
  }
276
 
277
  # ==========================
278
  # Gradio Interface
279
  # ==========================
280
  def gradio_interface(video_file):
281
+ if not video_file:
282
+ return {"error": "No video file uploaded"}, "", [], "", "", "No file uploaded."
283
  try:
 
 
 
284
  with open(video_file, "rb") as f:
285
  video_data = f.read()
 
286
  result = process_video(video_data)
287
  return (
288
+ result["violations"],
289
+ f"Safety Score: {result['score']}%",
290
+ result["snapshots"],
291
+ f"Salesforce Record ID: {result['salesforce_record_id'] or 'N/A'}",
292
+ f"Violation Details URL: {result['violation_details_url'] or 'N/A'}",
293
+ result["violation_details_content"]
294
  )
295
  except Exception as e:
296
+ logger.error(f"Error in Gradio interface: {e}")
297
  return {"error": str(e)}, "", [], "", "", "Error in processing."
298
 
299
  interface = gr.Interface(
 
305
  gr.JSON(label="Snapshots"),
306
  gr.Textbox(label="Salesforce Record ID"),
307
  gr.Textbox(label="Violation Details URL"),
308
+ gr.Textbox(label="Violation Details Content")
309
  ],
310
  title="Worksite Safety Violation Analyzer",
311
  description="Upload short site videos to detect safety violations (e.g., no helmet, no harness, unsafe posture)."
312
  )
313
 
314
  if __name__ == "__main__":
315
+ logger.info("Launching Safety Analyzer App...")
316
  interface.launch()