AI-RESEARCHER-2024 commited on
Commit
6ea3aa8
·
verified ·
1 Parent(s): e9c4ac2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +369 -297
app.py CHANGED
@@ -2,13 +2,13 @@ import spaces
2
 
3
  # Configure ZeroGPU
4
  @spaces.GPU
5
- def process_video_with_gpu(video, resize_option):
6
- """ZeroGPU-accelerated video processing"""
7
  # Create assessor inside the GPU function to avoid pickling issues
8
  from google import genai
9
  client = genai.Client(api_key=GOOGLE_API_KEY)
10
  assessor = CICE_Assessment(client)
11
- return process_video_core(video, resize_option, assessor)
12
 
13
  import gradio as gr
14
  from google import genai
@@ -32,23 +32,23 @@ import subprocess
32
  import shutil
33
 
34
  # Configure Google API Key from environment variable or Hugging Face secrets
35
- print("🔑 Setting up Google API Key...")
36
  GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
37
 
38
  if not GOOGLE_API_KEY:
39
  raise ValueError("GOOGLE_API_KEY environment variable is not set. Please set it in Hugging Face Spaces secrets.")
40
 
41
  client = genai.Client(api_key=GOOGLE_API_KEY)
42
- print("Google Generative AI configured successfully!")
43
 
44
- # Define the CICE Assessment Class
45
  class CICE_Assessment:
46
  def __init__(self, client):
47
  self.client = client
48
  self.model_name = "gemini-2.0-flash-exp"
49
 
50
- def analyze_video(self, video_path):
51
- """Analyze video using the 18-point CICE 2.0 assessment with specific behavioral cues"""
52
 
53
  try:
54
  # Determine mime type based on file extension
@@ -59,12 +59,12 @@ class CICE_Assessment:
59
  mime_type = 'video/mp4'
60
 
61
  # Upload video to Gemini
62
- print(f"📤 Uploading video to Gemini AI (type: {mime_type})...")
63
  with open(video_path, 'rb') as f:
64
  video_file = self.client.files.upload(file=f, config={'mime_type': mime_type})
65
 
66
  # Wait for processing
67
- print("Processing video (this may take 30-60 seconds)...")
68
  max_wait = 300
69
  wait_time = 0
70
  while video_file.state == "PROCESSING" and wait_time < max_wait:
@@ -75,103 +75,148 @@ class CICE_Assessment:
75
  if video_file.state == "FAILED":
76
  raise Exception("Video processing failed")
77
 
78
- print("🔍 Analyzing team interactions...")
79
 
80
- # CICE 2.0 Assessment Prompt
81
- prompt = """Analyze this healthcare team interaction video and provide a comprehensive assessment based on the CICE 2.0 instrument's 18 interprofessional competencies, looking for these SPECIFIC BEHAVIORAL CUES:
82
 
83
- For EACH competency, clearly state whether it was "OBSERVED" or "NOT OBSERVED" based on these specific behaviors:
84
-
85
- 1. IDENTIFIES FACTORS INFLUENCING HEALTH STATUS
86
- LOOK FOR: Team mentions allergy bracelet, fall-related trauma, multiple injuries, or states airway/breathing/circulation concerns out loud
87
-
88
- 2. IDENTIFIES TEAM GOALS FOR THE PATIENT
89
- LOOK FOR: Team verbalizes goals like: stabilize airway, CPR/AED, give epinephrine, control bleeding, preserve tooth, prepare EMS handoff
90
-
91
- 3. PRIORITIZES GOALS FOCUSED ON IMPROVING HEALTH OUTCOMES
92
- LOOK FOR: CPR/AED prioritized before bleeding/dental injury, EpiPen administered before addressing secondary injuries
93
-
94
- 4. VERBALIZES DISCIPLINE-SPECIFIC ROLE (PRE-BRIEF)
95
- LOOK FOR: Students acknowledge interprofessional communication expectations and scene safety review before scenario begins
96
-
97
- 5. OFFERS TO SEEK GUIDANCE FROM COLLEAGUES
98
- LOOK FOR: Peer-to-peer checks (e.g., dental to dental: confirm tooth storage; nursing to nursing: confirm CPR quality)
99
-
100
- 6. COMMUNICATES ABOUT COST-EFFECTIVE AND TIMELY CARE
101
- LOOK FOR: Team chooses readily available supplies (AED, saline, tourniquet) without delay, states need for rapid EMS transfer
102
-
103
- 7. DIRECTS QUESTIONS TO OTHER HEALTH PROFESSIONALS BASED ON EXPERTISE
104
- LOOK FOR: Asks discipline-specific expertise (e.g., "Dental—what do we do with the tooth?"), invites pharmacy/medical input on epinephrine use
105
-
106
- 8. AVOIDS DISCIPLINE-SPECIFIC TERMINOLOGY
107
- LOOK FOR: Uses plain language like "no pulse" instead of "asystole"
108
-
109
- 9. EXPLAINS DISCIPLINE-SPECIFIC TERMINOLOGY WHEN NECESSARY
110
- LOOK FOR: Clarifies medical/dental terms for others when necessary
111
-
112
- 10. COMMUNICATES ROLES AND RESPONSIBILITIES CLEARLY
113
- LOOK FOR: Announces assignments out loud: "I'll do compressions," "I'll call 911," "I'll document"
114
-
115
- 11. ENGAGES IN ACTIVE LISTENING
116
- LOOK FOR: Repeats back instructions ("Everyone clear for shock"), pauses to hear teammates' updates
117
-
118
- 12. SOLICITS AND ACKNOWLEDGES PERSPECTIVES
119
- LOOK FOR: Leader asks "Anything else we need to address?", responds to peer input respectfully
120
 
121
- 13. RECOGNIZES APPROPRIATE CONTRIBUTIONS
122
- LOOK FOR: Affirms correct actions verbally ("Good catch on allergy bracelet"), non-verbal acknowledgment (nodding, thumbs up)
123
 
124
- 14. RESPECTFUL OF OTHER TEAM MEMBERS
125
- LOOK FOR: Listens without interrupting, values input across professions
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
- 15. COLLABORATIVELY WORKS THROUGH INTERPROFESSIONAL CONFLICTS
128
- LOOK FOR: Negotiates intervention priorities (airway vs. bleeding) respectfully
 
 
 
 
129
 
130
- 16. REFLECTS ON STRENGTHS OF TEAM INTERACTIONS (POST-BRIEF)
131
- LOOK FOR: Notes strong teamwork, communication, or role clarity after the scenario
132
 
133
- 17. REFLECTS ON CHALLENGES OF TEAM INTERACTIONS (POST-BRIEF)
134
- LOOK FOR: Identifies confusion, delays, or role overlap in debriefing
135
 
136
- 18. IDENTIFIES HOW TO IMPROVE TEAM EFFECTIVENESS (POST-BRIEF)
137
- LOOK FOR: Suggests faster role assignment, consistent closed-loop communication, earlier epi use
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  STRUCTURE YOUR RESPONSE AS FOLLOWS:
140
 
141
- ## OVERALL ASSESSMENT
142
- Brief overview of the team interaction quality.
143
 
144
- ## DETAILED COMPETENCY EVALUATION
145
- For each of the 18 competencies, format as:
146
- Competency [number]: [name]
147
- Status: [OBSERVED/NOT OBSERVED]
148
- Evidence: [Specific behavioral cue observed or explanation of absence]
 
 
149
 
150
- ## STRENGTHS
151
- Top 3-5 key strengths with specific examples
152
 
153
- ## AREAS FOR IMPROVEMENT
154
- Top 3-5 areas needing work with specific suggestions
155
 
156
- ## AUDIO SUMMARY
157
- [Create a concise 60-second spoken summary focusing on: overall performance level, top 3 strengths, top 3 areas for improvement, and 2 key actionable recommendations. Write this in a natural, conversational tone suitable for text-to-speech narration.]
158
-
159
- ## FINAL SCORE
160
- Competencies Observed: X/18
161
- Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (50-69%)/Needs Improvement (0-49%)]"""
 
162
 
163
- response = self.client.models.generate_content(
164
- model=self.model_name,
165
- contents=[
166
- types.Part.from_uri(file_uri=video_file.uri, mime_type=video_file.mime_type),
167
- prompt
168
- ]
169
- )
170
- print("✅ Analysis complete!")
171
- return response.text
172
 
173
- except Exception as e:
174
- return f"Error during analysis: {str(e)}"
 
 
 
175
 
176
  def generate_audio_feedback(self, text):
177
  """Generate a concise 1-minute audio feedback summary"""
@@ -191,11 +236,11 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
191
  clean_text = re.sub(r'[-•·]\s+', '', clean_text)
192
 
193
  # Add introduction and conclusion for better audio experience
194
- audio_script = f"""CICE Healthcare Team Assessment Summary.
195
 
196
  {clean_text}
197
 
198
- Please refer to the detailed written report for complete competency evaluation and specific recommendations.
199
  End of audio summary."""
200
 
201
  # Generate audio with gTTS
@@ -209,99 +254,61 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
209
 
210
  return temp_audio.name
211
  except Exception as e:
212
- print(f"⚠️ Audio generation failed: {str(e)}")
213
  return None
214
 
215
  def create_brief_summary(self, text):
216
  """Create a brief summary if AUDIO SUMMARY section is not found"""
217
-
218
- # Parse scores
219
- observed_count = text.lower().count("observed") - text.lower().count("not observed")
220
- total = 18
221
- percentage = (observed_count / total) * 100
222
-
223
- # Determine performance level
224
- if percentage >= 85:
225
- level = "Exemplary"
226
- elif percentage >= 70:
227
- level = "Proficient"
228
- elif percentage >= 50:
229
- level = "Developing"
230
- else:
231
- level = "Needs Improvement"
232
-
233
- summary = f"""The team demonstrated {level} performance with {observed_count} out of {total} competencies observed,
234
- achieving {percentage:.0f} percent overall.
235
-
236
- Key strengths included strong team communication and role clarity.
237
-
238
- Areas for improvement include enhancing active listening and conflict resolution skills.
239
-
240
- The team should focus on pre-briefing protocols and post-scenario debriefing to enhance future performance.
241
- Emphasis should be placed on clear role assignment and closed-loop communication during critical interventions."""
242
 
243
  return summary
244
 
245
- def parse_assessment_scores(self, assessment_text):
246
- """Parse assessment text to extract scores"""
247
 
248
- # Method 1: Look for "Status: OBSERVED" vs "Status: NOT OBSERVED" patterns
249
  import re
250
 
251
- # Find all status lines
252
- status_pattern = r'Status:\s*(OBSERVED|NOT OBSERVED)'
253
- matches = re.findall(status_pattern, assessment_text, re.IGNORECASE)
254
-
255
- # Count only "OBSERVED" (not "NOT OBSERVED")
256
- observed_count = sum(1 for match in matches if match.upper() == "OBSERVED")
257
 
258
- # If no matches found with Status: pattern, try alternative parsing
259
- if len(matches) == 0:
260
- # Alternative: Look for competency lines with OBSERVED/NOT OBSERVED
261
- lines = assessment_text.split('\n')
262
- observed_count = 0
263
-
264
- for i, line in enumerate(lines):
265
- # Look for competency indicators followed by status
266
- if 'Competency' in line and i + 1 < len(lines):
267
- next_line = lines[i + 1]
268
- # Check if the status line indicates OBSERVED (not NOT OBSERVED)
269
- if 'OBSERVED' in next_line.upper() and 'NOT OBSERVED' not in next_line.upper():
270
- observed_count += 1
271
-
272
- # If still no matches, use a more robust pattern
273
- if observed_count == 0:
274
- # Count lines that say "OBSERVED" but not "NOT OBSERVED"
275
- for line in lines:
276
- # Clean line for better matching
277
- clean_line = line.strip().upper()
278
- if clean_line.startswith('STATUS:'):
279
- if 'NOT OBSERVED' in clean_line:
280
- continue
281
- elif 'OBSERVED' in clean_line:
282
- observed_count += 1
283
 
284
- total_competencies = 18
285
- percentage = (observed_count / total_competencies) * 100 if total_competencies > 0 else 0
286
 
287
- # Professional color scheme with better contrast
288
- if percentage >= 85:
289
  level = "Exemplary"
290
  color = "#0F766E" # Deep teal
291
- elif percentage >= 70:
292
  level = "Proficient"
293
  color = "#1E40AF" # Professional blue
294
- elif percentage >= 50:
295
  level = "Developing"
296
  color = "#EA580C" # Professional orange
297
  else:
298
  level = "Needs Improvement"
299
  color = "#B91C1C" # Deep red
300
 
301
- return observed_count, total_competencies, percentage, level, color
302
 
303
- def generate_pdf_report(self, assessment_text):
304
- """Generate a PDF report from the assessment text"""
305
 
306
  try:
307
  # Create a temporary file for the PDF
@@ -326,7 +333,7 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
326
  'CustomTitle',
327
  parent=styles['Heading1'],
328
  fontSize=24,
329
- textColor=HexColor('#111827'), # Darker gray for better readability
330
  spaceAfter=30,
331
  alignment=TA_CENTER
332
  )
@@ -335,7 +342,7 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
335
  'CustomHeading',
336
  parent=styles['Heading2'],
337
  fontSize=14,
338
- textColor=HexColor('#1E40AF'), # Professional blue
339
  spaceAfter=12,
340
  spaceBefore=12,
341
  bold=True
@@ -350,13 +357,23 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
350
  )
351
 
352
  # Add title
353
- elements.append(Paragraph("CICE 2.0 Healthcare Team Assessment Report", title_style))
 
354
  elements.append(Spacer(1, 12))
355
 
356
  # Add timestamp
357
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
358
  elements.append(Paragraph(f"<b>Assessment Date:</b> {timestamp}", body_style))
359
  elements.append(Spacer(1, 20))
 
 
 
 
 
 
 
 
 
360
 
361
  # Process the assessment text into PDF-friendly format
362
  lines = assessment_text.split('\n')
@@ -370,15 +387,12 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
370
  # Major heading
371
  heading_text = line.replace('##', '').strip()
372
  elements.append(Paragraph(heading_text, heading_style))
373
- elif line.startswith('Competency'):
374
- # Competency item
375
- elements.append(Paragraph(f"<b>{line}</b>", body_style))
376
- elif line.startswith('Status:') or line.startswith('Evidence:'):
377
- # Sub-items
378
- elements.append(Paragraph(line, body_style))
379
  else:
380
- # Regular text
381
- # Escape special characters for PDF
382
  line = line.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
383
  elements.append(Paragraph(line, body_style))
384
 
@@ -389,13 +403,15 @@ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (
389
  return temp_pdf.name
390
 
391
  except Exception as e:
392
- print(f"⚠️ PDF generation failed: {str(e)}")
393
  # Fallback to text file
394
  temp_txt = tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w')
395
- temp_txt.write("CICE 2.0 Healthcare Team Interaction Assessment\n")
 
396
  temp_txt.write("="*60 + "\n")
397
  temp_txt.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
398
  temp_txt.write("="*60 + "\n\n")
 
399
  temp_txt.write(assessment_text)
400
  temp_txt.close()
401
  return temp_txt.name
@@ -421,7 +437,7 @@ def resize_video(input_path, target_width, target_height):
421
  # Create video writer with new dimensions
422
  out = cv2.VideoWriter(temp_output.name, fourcc, fps, (target_width, target_height))
423
 
424
- print(f"📐 Resizing video to {target_width}x{target_height}...")
425
  frame_count = 0
426
 
427
  while True:
@@ -437,11 +453,11 @@ def resize_video(input_path, target_width, target_height):
437
  cap.release()
438
  out.release()
439
 
440
- print(f"Video resized successfully ({frame_count} frames)")
441
  return temp_output.name
442
 
443
  except Exception as e:
444
- print(f"⚠️ Video resize failed: {str(e)}")
445
  return input_path # Return original if resize fails
446
 
447
  def get_video_info(video_path):
@@ -496,7 +512,7 @@ def show_saving_status(video):
496
  </style>
497
  <div style="text-align: center; color: white;">
498
  <div style="font-size: 24px; font-weight: bold; margin-bottom: 10px;">
499
- 📹 Processing Your Recording...
500
  </div>
501
  <div style="font-size: 16px; opacity: 0.95;">
502
  Saving video file • Preparing for download
@@ -530,7 +546,7 @@ def save_recorded_video_with_status(video):
530
  <div style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); padding: 15px; border-radius: 10px; margin: 20px 0;">
531
  <div style="text-align: center; color: white;">
532
  <div style="font-size: 20px; font-weight: bold;">
533
- Video Saved Successfully!
534
  </div>
535
  <div style="font-size: 14px; margin-top: 5px; opacity: 0.95;">
536
  Ready for download • Click "Analyze Video" to assess
@@ -539,16 +555,16 @@ def save_recorded_video_with_status(video):
539
  </div>
540
  """
541
 
542
- print(f"📹 Video saved: {output_filename}")
543
  return temp_output.name, gr.update(value=success_html, visible=True)
544
 
545
  except Exception as e:
546
- print(f"⚠️ Failed to save video: {str(e)}")
547
  error_html = """
548
  <div style="background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); padding: 15px; border-radius: 10px; margin: 20px 0;">
549
  <div style="text-align: center; color: white;">
550
  <div style="font-size: 20px; font-weight: bold;">
551
- ⚠️ Error Saving Video
552
  </div>
553
  <div style="font-size: 14px; margin-top: 5px;">
554
  Please try recording again
@@ -558,15 +574,9 @@ def save_recorded_video_with_status(video):
558
  """
559
  return None, gr.update(value=error_html, visible=True)
560
 
561
- # Function to hide status after a delay
562
- def hide_status_after_delay():
563
- """Hide the status bar after showing success"""
564
- time.sleep(3) # Wait 3 seconds
565
- return gr.update(value="", visible=False)
566
-
567
  # Define the core processing function (separate from GPU wrapper)
568
- def process_video_core(video, resize_option, assessor):
569
- """Process uploaded or recorded video"""
570
 
571
  if video is None:
572
  return "Please upload or record a video first.", None, None, None
@@ -575,11 +585,11 @@ def process_video_core(video, resize_option, assessor):
575
  # Get original video info
576
  orig_width, orig_height, fps, frame_count = get_video_info(video)
577
  if orig_width and orig_height:
578
- print(f"📹 Original video: {orig_width}x{orig_height} @ {fps}fps ({frame_count} frames)")
579
 
580
  # Get file size
581
  file_size_mb = os.path.getsize(video) / (1024 * 1024)
582
- print(f"📹 Processing video ({file_size_mb:.1f}MB)...")
583
 
584
  # Apply resizing based on user selection
585
  video_to_process = video
@@ -603,12 +613,13 @@ def process_video_core(video, resize_option, assessor):
603
 
604
  # Check new file size
605
  new_file_size_mb = os.path.getsize(video_to_process) / (1024 * 1024)
606
- print(f"📦 Resized video: {new_file_size_mb:.1f}MB (saved {file_size_mb - new_file_size_mb:.1f}MB)")
607
 
608
- # Start assessment
609
- print("🏥 Starting CICE 2.0 Healthcare Team Assessment...")
 
610
 
611
- assessment_result = assessor.analyze_video(video_to_process)
612
 
613
  # Clean up temporary resized file if created
614
  if temp_resized_file and temp_resized_file != video:
@@ -621,29 +632,29 @@ def process_video_core(video, resize_option, assessor):
621
  return assessment_result, None, None, None
622
 
623
  # Generate 1-minute audio feedback
624
- print("🔊 Generating 1-minute audio summary...")
625
  audio_path = assessor.generate_audio_feedback(assessment_result)
626
 
627
- # Generate PDF report
628
- print("📄 Generating PDF report...")
629
- pdf_path = assessor.generate_pdf_report(assessment_result)
630
 
631
  # Parse scores for visual summary
632
- observed, total, percentage, level, color = assessor.parse_assessment_scores(assessment_result)
633
 
634
- # Create enhanced visual summary HTML with professional colors
635
  summary_html = f"""
636
  <div style="max-width:800px; margin:20px auto; padding:30px; border-radius:15px; box-shadow:0 2px 10px rgba(0,0,0,0.08); background:white;">
637
- <h2 style="text-align:center; color:#111827; margin-bottom:30px; font-weight:600;">CICE 2.0 Assessment Summary</h2>
638
 
639
  <div style="display:flex; justify-content:space-around; margin:30px 0;">
640
  <div style="text-align:center;">
641
- <div style="font-size:48px; font-weight:bold; color:{color};">{observed}/{total}</div>
642
- <div style="color:#4B5563; margin-top:10px; font-weight:500;">Competencies Observed</div>
643
  </div>
644
  <div style="text-align:center;">
645
  <div style="font-size:48px; font-weight:bold; color:{color};">{percentage:.0f}%</div>
646
- <div style="color:#4B5563; margin-top:10px; font-weight:500;">Overall Score</div>
647
  </div>
648
  </div>
649
 
@@ -652,43 +663,66 @@ def process_video_core(video, resize_option, assessor):
652
  </div>
653
 
654
  <div style="margin-top:30px;">
655
- <h3 style="color:#111827; margin-bottom:20px; font-weight:600;">🎯 Key Behavioral Indicators Assessed:</h3>
656
-
657
- <div style="background:#F8FAFC; padding:15px; border-radius:10px; margin:15px 0; border:1px solid #E2E8F0;">
658
- <h4 style="color:#0F766E; margin-top:0; font-weight:600;">✅ Critical Actions</h4>
659
- <ul style="line-height:1.8; margin:10px 0;">
660
- <li style="color:#374151;">CPR/AED prioritization</li>
661
- <li style="color:#374151;">Epinephrine administration timing</li>
662
- <li style="color:#374151;">Clear role assignments ("I'll do compressions")</li>
663
- <li style="color:#374151;">Closed-loop communication</li>
664
- </ul>
665
- </div>
666
-
667
- <div style="background:#F8FAFC; padding:15px; border-radius:10px; margin:15px 0; border:1px solid #E2E8F0;">
668
- <h4 style="color:#1E40AF; margin-top:0; font-weight:600;">🗣️ Communication Markers</h4>
669
- <ul style="line-height:1.8; margin:10px 0;">
670
- <li style="color:#374151;">Plain language use (avoiding medical jargon)</li>
671
- <li style="color:#374151;">Active listening (repeating back instructions)</li>
672
- <li style="color:#374151;">Soliciting input ("Anything else we need?")</li>
673
- <li style="color:#374151;">Recognizing contributions ("Good catch!")</li>
674
- </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  </div>
 
676
 
677
- <div style="background:#F8FAFC; padding:15px; border-radius:10px; margin:15px 0; border:1px solid #E2E8F0;">
678
- <h4 style="color:#6B21A8; margin-top:0; font-weight:600;">🔄 Team Dynamics</h4>
679
- <ul style="line-height:1.8; margin:10px 0;">
680
- <li style="color:#374151;">Pre-brief safety review</li>
681
- <li style="color:#374151;">Peer-to-peer verification</li>
682
- <li style="color:#374151;">Respectful conflict resolution</li>
683
- <li style="color:#374151;">Post-brief reflection on strengths/challenges</li>
684
- </ul>
685
  </div>
686
  </div>
687
 
688
  <div style="margin-top:30px; padding:20px; background:#FFF7ED; border-radius:10px; border-left:4px solid #EA580C;">
689
  <p style="text-align:center; color:#431407; margin:0; font-weight:600;">
690
- 🔊 Listen to the 1-minute audio summary for key findings<br>
691
- 📄 Download the PDF report for complete documentation
692
  </p>
693
  </div>
694
  </div>
@@ -697,37 +731,34 @@ def process_video_core(video, resize_option, assessor):
697
  return assessment_result, summary_html, audio_path, pdf_path
698
 
699
  except Exception as e:
700
- error_msg = f"Error during processing: {str(e)}"
701
  print(error_msg)
702
  return error_msg, None, None, None
703
 
704
  # Wrapper function that calls the GPU-accelerated version
705
- def process_video(video, resize_option):
706
- """Wrapper function to call GPU-accelerated processing"""
707
- return process_video_with_gpu(video, resize_option)
708
 
709
- # Create and launch the Gradio interface
710
- print("🚀 Launching CICE 2.0 Healthcare Assessment Tool...")
711
 
712
- with gr.Blocks(title="CICE 2.0 Healthcare Assessment Tool", theme=gr.themes.Soft()) as demo:
713
 
714
  gr.Markdown("""
715
- # 🏥 CICE 2.0 Healthcare Team Assessment Tool
716
-
717
- **Analyze healthcare team interactions using specific behavioral cues from the 18-point CICE 2.0 framework**
718
 
719
- This tool evaluates critical team behaviors including:
720
- - Values/ethics for interprofessional practice
721
- - Roles/responsibilities
722
- - Interprofessional communication
723
- - Teams and teamwork
724
 
725
  ---
726
  """)
727
 
728
  with gr.Row():
729
  with gr.Column(scale=1):
730
- gr.Markdown("### 📹 Video Input")
731
 
732
  # Video resolution dropdown
733
  resize_dropdown = gr.Dropdown(
@@ -749,8 +780,8 @@ with gr.Blocks(title="CICE 2.0 Healthcare Assessment Tool", theme=gr.themes.Soft
749
  include_audio=True,
750
  interactive=True,
751
  webcam_constraints={"video": {"width": 800, "height": 600}},
752
- autoplay=False, # Disable autoplay for faster loading
753
- show_download_button=True # Show download button immediately
754
  )
755
 
756
  # Status bar for immediate feedback
@@ -762,41 +793,74 @@ with gr.Blocks(title="CICE 2.0 Healthcare Assessment Tool", theme=gr.themes.Soft
762
 
763
  # Add download component for recorded videos
764
  recorded_video_download = gr.File(
765
- label="📥 Download Recorded Video",
766
  interactive=False,
767
  visible=False
768
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
 
770
  gr.Markdown("""
771
- ### 📝 Instructions:
772
- 1. **Select video resolution** (lower = faster processing)
773
- 2. **Upload** a pre-recorded video or **Record** using your webcam
774
- 3. Video will be saved and downloadable immediately after recording stops
775
- 4. Click **Analyze Video** (on the right) to start the assessment
776
- 5. Wait for the AI to process (1-2 minutes)
777
- 6. Listen to the **1-minute audio summary**
778
- 7. Download the **PDF report** for documentation
779
-
780
- **Video Resolution Guide:**
781
- - **640x480**: Fastest processing, uses least quota
782
- - **800x600**: Recommended balance (default)
783
- - **1280x720**: Best quality, takes longer
784
- - **Original**: No resizing (slowest)
785
-
786
- **Key Behaviors Assessed:**
787
- - Allergy/medical history identification
788
- - CPR/AED prioritization
789
- - Clear role assignments
790
- - Plain language communication
791
- - Active listening behaviors
792
- - Team respect and conflict resolution
793
  """)
794
 
795
  with gr.Column(scale=2):
796
- gr.Markdown("### 📊 Assessment Results")
797
 
798
- # Move analyze button here (to the right column)
799
- analyze_btn = gr.Button("🔍 Analyze Video", variant="primary", size="lg")
800
 
801
  # Visual summary
802
  summary_output = gr.HTML(
@@ -806,21 +870,21 @@ with gr.Blocks(title="CICE 2.0 Healthcare Assessment Tool", theme=gr.themes.Soft
806
 
807
  # Audio feedback - downloadable
808
  audio_output = gr.Audio(
809
- label="🔊 1-Minute Audio Summary (Downloadable)",
810
  type="filepath",
811
  interactive=False
812
  )
813
 
814
  # PDF report - downloadable
815
  pdf_output = gr.File(
816
- label="📄 Download Full PDF Report",
817
  interactive=False,
818
  file_types=[".pdf", ".txt"]
819
  )
820
 
821
  # Detailed assessment text
822
  assessment_output = gr.Textbox(
823
- label="Detailed CICE 2.0 Assessment (Text View)",
824
  lines=20,
825
  max_lines=30,
826
  interactive=False,
@@ -830,22 +894,22 @@ with gr.Blocks(title="CICE 2.0 Healthcare Assessment Tool", theme=gr.themes.Soft
830
  # Footer
831
  gr.Markdown("""
832
  ---
833
- ### About This Assessment
834
- This tool uses Google's Gemini AI to identify specific behavioral markers that indicate effective interprofessional collaboration
835
- in healthcare settings. The assessment focuses on observable actions such as:
836
- - Verbal role assignments ("I'll do compressions")
837
- - Recognition phrases ("Good catch on the allergy bracelet")
838
- - Plain language use instead of medical jargon
839
- - Pre-brief and post-brief team discussions
840
-
841
- **Output Files:**
842
- - 📊 1-minute audio summary (MP3 format)
843
- - 📄 Complete PDF assessment report
844
 
845
  **Powered by Google Gemini 2.0 Flash | ZeroGPU on HuggingFace Spaces**
846
  """)
847
 
848
- # Auto-save video when recording stops with immediate status feedback
849
  video_input.stop_recording(
850
  fn=show_saving_status,
851
  inputs=[video_input],
@@ -870,10 +934,18 @@ with gr.Blocks(title="CICE 2.0 Healthcare Assessment Tool", theme=gr.themes.Soft
870
  outputs=[status_bar]
871
  )
872
 
873
- # Connect the analyze button
874
  analyze_btn.click(
875
  fn=process_video,
876
- inputs=[video_input, resize_dropdown],
 
 
 
 
 
 
 
 
877
  outputs=[assessment_output, summary_output, audio_output, pdf_output],
878
  api_name="analyze"
879
  )
 
2
 
3
  # Configure ZeroGPU
4
  @spaces.GPU
5
+ def process_video_with_gpu(video, resize_option, param1, param2, param3, param4, param5):
6
+ """ZeroGPU-accelerated video processing with custom parameters"""
7
  # Create assessor inside the GPU function to avoid pickling issues
8
  from google import genai
9
  client = genai.Client(api_key=GOOGLE_API_KEY)
10
  assessor = CICE_Assessment(client)
11
+ return process_video_core(video, resize_option, assessor, param1, param2, param3, param4, param5)
12
 
13
  import gradio as gr
14
  from google import genai
 
32
  import shutil
33
 
34
  # Configure Google API Key from environment variable or Hugging Face secrets
35
+ print("Setting up Google API Key...")
36
  GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
37
 
38
  if not GOOGLE_API_KEY:
39
  raise ValueError("GOOGLE_API_KEY environment variable is not set. Please set it in Hugging Face Spaces secrets.")
40
 
41
  client = genai.Client(api_key=GOOGLE_API_KEY)
42
+ print("Google Generative AI configured successfully!")
43
 
44
+ # Define the CICE Assessment Class with parameters
45
  class CICE_Assessment:
46
  def __init__(self, client):
47
  self.client = client
48
  self.model_name = "gemini-2.0-flash-exp"
49
 
50
+ def analyze_video(self, video_path, param1, param2, param3, param4, param5):
51
+ """Analyze video using customizable assessment parameters"""
52
 
53
  try:
54
  # Determine mime type based on file extension
 
59
  mime_type = 'video/mp4'
60
 
61
  # Upload video to Gemini
62
+ print(f"Uploading video to Gemini AI (type: {mime_type})...")
63
  with open(video_path, 'rb') as f:
64
  video_file = self.client.files.upload(file=f, config={'mime_type': mime_type})
65
 
66
  # Wait for processing
67
+ print("Processing video (this may take 30-60 seconds)...")
68
  max_wait = 300
69
  wait_time = 0
70
  while video_file.state == "PROCESSING" and wait_time < max_wait:
 
75
  if video_file.state == "FAILED":
76
  raise Exception("Video processing failed")
77
 
78
+ print("Analyzing team interactions with custom parameters...")
79
 
80
+ # Build dynamic assessment prompt based on parameters
81
+ prompt = self.build_assessment_prompt(param1, param2, param3, param4, param5)
82
 
83
+ response = self.client.models.generate_content(
84
+ model=self.model_name,
85
+ contents=[
86
+ types.Part.from_uri(file_uri=video_file.uri, mime_type=video_file.mime_type),
87
+ prompt
88
+ ]
89
+ )
90
+ print("Analysis complete!")
91
+ return response.text, param1, param2, param3, param4, param5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ except Exception as e:
94
+ return f"Error during analysis: {str(e)}", param1, param2, param3, param4, param5
95
 
96
+ def build_assessment_prompt(self, communication_weight, teamwork_weight, safety_weight, emergency_weight, leadership_weight):
97
+ """Build a dynamic prompt based on user-selected parameters"""
98
+
99
+ # Normalize weights
100
+ total_weight = communication_weight + teamwork_weight + safety_weight + emergency_weight + leadership_weight
101
+ if total_weight == 0:
102
+ total_weight = 1 # Avoid division by zero
103
+
104
+ comm_pct = (communication_weight / total_weight) * 100
105
+ team_pct = (teamwork_weight / total_weight) * 100
106
+ safety_pct = (safety_weight / total_weight) * 100
107
+ emerg_pct = (emergency_weight / total_weight) * 100
108
+ lead_pct = (leadership_weight / total_weight) * 100
109
+
110
+ prompt = f"""Analyze this healthcare team interaction video with the following CUSTOMIZED EVALUATION PARAMETERS:
111
 
112
+ EVALUATION WEIGHTS (Total 100%):
113
+ 1. COMMUNICATION EFFECTIVENESS: {comm_pct:.1f}% weight
114
+ 2. TEAMWORK & COLLABORATION: {team_pct:.1f}% weight
115
+ 3. SAFETY PROTOCOLS: {safety_pct:.1f}% weight
116
+ 4. EMERGENCY RESPONSE: {emerg_pct:.1f}% weight
117
+ 5. LEADERSHIP & ROLES: {lead_pct:.1f}% weight
118
 
119
+ Please evaluate the video based on these weighted priorities:
 
120
 
121
+ """
 
122
 
123
+ # Add detailed criteria based on weights
124
+ criteria_sections = []
125
+
126
+ if communication_weight > 0:
127
+ criteria_sections.append(f"""
128
+ ## COMMUNICATION EFFECTIVENESS (Weight: {communication_weight}/10)
129
+ Evaluate:
130
+ - Clear verbal communication
131
+ - Active listening behaviors
132
+ - Closed-loop communication
133
+ - Use of plain language vs. jargon
134
+ - Information sharing quality
135
+ """)
136
+
137
+ if teamwork_weight > 0:
138
+ criteria_sections.append(f"""
139
+ ## TEAMWORK & COLLABORATION (Weight: {teamwork_weight}/10)
140
+ Evaluate:
141
+ - Team coordination
142
+ - Mutual support behaviors
143
+ - Collaborative problem-solving
144
+ - Respect for team members
145
+ - Conflict resolution
146
+ """)
147
+
148
+ if safety_weight > 0:
149
+ criteria_sections.append(f"""
150
+ ## SAFETY PROTOCOLS (Weight: {safety_weight}/10)
151
+ Evaluate:
152
+ - Patient safety measures
153
+ - Infection control practices
154
+ - Equipment safety checks
155
+ - Environmental awareness
156
+ - Risk identification
157
+ """)
158
+
159
+ if emergency_weight > 0:
160
+ criteria_sections.append(f"""
161
+ ## EMERGENCY RESPONSE (Weight: {emergency_weight}/10)
162
+ Evaluate:
163
+ - Response time and urgency
164
+ - Priority setting (ABC - Airway, Breathing, Circulation)
165
+ - Emergency protocol adherence
166
+ - Critical intervention timing
167
+ - Resource utilization
168
+ """)
169
+
170
+ if leadership_weight > 0:
171
+ criteria_sections.append(f"""
172
+ ## LEADERSHIP & ROLES (Weight: {lead_weight}/10)
173
+ Evaluate:
174
+ - Clear role assignments
175
+ - Leadership presence
176
+ - Decision-making clarity
177
+ - Delegation effectiveness
178
+ - Team guidance
179
+ """)
180
+
181
+ prompt += "".join(criteria_sections)
182
+
183
+ prompt += f"""
184
 
185
  STRUCTURE YOUR RESPONSE AS FOLLOWS:
186
 
187
+ ## OVERALL WEIGHTED ASSESSMENT
188
+ Provide an overall score based on the weighted parameters above.
189
 
190
+ ## DETAILED EVALUATION BY PARAMETER
191
+ For each parameter with weight > 0, provide:
192
+ - Parameter Name: [Name]
193
+ - Weight: [X/10]
194
+ - Score: [X/10]
195
+ - Observations: [Specific behaviors observed]
196
+ - Recommendations: [Specific improvements]
197
 
198
+ ## KEY STRENGTHS
199
+ Top 3-5 strengths observed (prioritize based on weighted parameters)
200
 
201
+ ## CRITICAL IMPROVEMENTS NEEDED
202
+ Top 3-5 areas needing improvement (prioritize based on weighted parameters)
203
 
204
+ ## WEIGHTED FINAL SCORE
205
+ Calculate the weighted average score:
206
+ - Communication: {communication_weight}/10 weight × [score]/10
207
+ - Teamwork: {teamwork_weight}/10 weight × [score]/10
208
+ - Safety: {safety_weight}/10 weight × [score]/10
209
+ - Emergency Response: {emergency_weight}/10 weight × [score]/10
210
+ - Leadership: {leadership_weight}/10 weight × [score]/10
211
 
212
+ TOTAL WEIGHTED SCORE: [X]/10
213
+ Performance Level: [Exemplary (8.5-10)/Proficient (7-8.4)/Developing (5-6.9)/Needs Improvement (0-4.9)]
 
 
 
 
 
 
 
214
 
215
+ ## AUDIO SUMMARY
216
+ [Create a 60-second spoken summary focusing on the overall weighted score, top strengths in high-weight areas, critical improvements in high-weight areas, and 2-3 actionable recommendations. Write in natural, conversational tone for text-to-speech.]
217
+ """
218
+
219
+ return prompt
220
 
221
  def generate_audio_feedback(self, text):
222
  """Generate a concise 1-minute audio feedback summary"""
 
236
  clean_text = re.sub(r'[-•·]\s+', '', clean_text)
237
 
238
  # Add introduction and conclusion for better audio experience
239
+ audio_script = f"""Healthcare Team Assessment Summary.
240
 
241
  {clean_text}
242
 
243
+ Please refer to the detailed written report for complete evaluation and specific recommendations.
244
  End of audio summary."""
245
 
246
  # Generate audio with gTTS
 
254
 
255
  return temp_audio.name
256
  except Exception as e:
257
+ print(f"Audio generation failed: {str(e)}")
258
  return None
259
 
260
  def create_brief_summary(self, text):
261
  """Create a brief summary if AUDIO SUMMARY section is not found"""
262
+
263
+ summary = f"""The team assessment has been completed based on your customized evaluation parameters.
264
+
265
+ The analysis focused on the specific areas you prioritized, with weighted scores reflecting
266
+ the importance you assigned to each parameter.
267
+
268
+ Key strengths were identified in the high-priority areas, and recommendations have been
269
+ provided for critical improvements.
270
+
271
+ Please review the detailed report for specific behavioral observations and actionable feedback
272
+ tailored to your evaluation criteria."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  return summary
275
 
276
+ def parse_assessment_scores(self, assessment_text, param1, param2, param3, param4, param5):
277
+ """Parse assessment text to extract weighted scores"""
278
 
279
+ # Try to extract the weighted score from the text
280
  import re
281
 
282
+ # Look for "TOTAL WEIGHTED SCORE: X/10" pattern
283
+ score_pattern = r'TOTAL WEIGHTED SCORE:\s*([0-9.]+)/10'
284
+ match = re.search(score_pattern, assessment_text, re.IGNORECASE)
 
 
 
285
 
286
+ if match:
287
+ weighted_score = float(match.group(1))
288
+ else:
289
+ # Fallback calculation
290
+ weighted_score = 7.5 # Default middle score
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
+ percentage = (weighted_score / 10) * 100
 
293
 
294
+ # Determine performance level and color based on score
295
+ if weighted_score >= 8.5:
296
  level = "Exemplary"
297
  color = "#0F766E" # Deep teal
298
+ elif weighted_score >= 7:
299
  level = "Proficient"
300
  color = "#1E40AF" # Professional blue
301
+ elif weighted_score >= 5:
302
  level = "Developing"
303
  color = "#EA580C" # Professional orange
304
  else:
305
  level = "Needs Improvement"
306
  color = "#B91C1C" # Deep red
307
 
308
+ return weighted_score, percentage, level, color
309
 
310
+ def generate_pdf_report(self, assessment_text, param1, param2, param3, param4, param5):
311
+ """Generate a PDF report from the assessment text with parameter information"""
312
 
313
  try:
314
  # Create a temporary file for the PDF
 
333
  'CustomTitle',
334
  parent=styles['Heading1'],
335
  fontSize=24,
336
+ textColor=HexColor('#111827'),
337
  spaceAfter=30,
338
  alignment=TA_CENTER
339
  )
 
342
  'CustomHeading',
343
  parent=styles['Heading2'],
344
  fontSize=14,
345
+ textColor=HexColor('#1E40AF'),
346
  spaceAfter=12,
347
  spaceBefore=12,
348
  bold=True
 
357
  )
358
 
359
  # Add title
360
+ elements.append(Paragraph("Healthcare Team Assessment Report", title_style))
361
+ elements.append(Paragraph("(Customized Evaluation Parameters)", body_style))
362
  elements.append(Spacer(1, 12))
363
 
364
  # Add timestamp
365
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
366
  elements.append(Paragraph(f"<b>Assessment Date:</b> {timestamp}", body_style))
367
  elements.append(Spacer(1, 20))
368
+
369
+ # Add parameter settings
370
+ elements.append(Paragraph("<b>Evaluation Parameters Used:</b>", heading_style))
371
+ elements.append(Paragraph(f"• Communication Effectiveness: {param1}/10", body_style))
372
+ elements.append(Paragraph(f"• Teamwork & Collaboration: {param2}/10", body_style))
373
+ elements.append(Paragraph(f"• Safety Protocols: {param3}/10", body_style))
374
+ elements.append(Paragraph(f"• Emergency Response: {param4}/10", body_style))
375
+ elements.append(Paragraph(f"• Leadership & Roles: {param5}/10", body_style))
376
+ elements.append(Spacer(1, 20))
377
 
378
  # Process the assessment text into PDF-friendly format
379
  lines = assessment_text.split('\n')
 
387
  # Major heading
388
  heading_text = line.replace('##', '').strip()
389
  elements.append(Paragraph(heading_text, heading_style))
390
+ elif line.startswith('#'):
391
+ # Sub-heading
392
+ heading_text = line.replace('#', '').strip()
393
+ elements.append(Paragraph(heading_text, body_style))
 
 
394
  else:
395
+ # Regular text - escape special characters for PDF
 
396
  line = line.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
397
  elements.append(Paragraph(line, body_style))
398
 
 
403
  return temp_pdf.name
404
 
405
  except Exception as e:
406
+ print(f"PDF generation failed: {str(e)}")
407
  # Fallback to text file
408
  temp_txt = tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w')
409
+ temp_txt.write("Healthcare Team Assessment Report\n")
410
+ temp_txt.write("(Customized Evaluation Parameters)\n")
411
  temp_txt.write("="*60 + "\n")
412
  temp_txt.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
413
  temp_txt.write("="*60 + "\n\n")
414
+ temp_txt.write(f"Parameters: Communication={param1}, Teamwork={param2}, Safety={param3}, Emergency={param4}, Leadership={param5}\n\n")
415
  temp_txt.write(assessment_text)
416
  temp_txt.close()
417
  return temp_txt.name
 
437
  # Create video writer with new dimensions
438
  out = cv2.VideoWriter(temp_output.name, fourcc, fps, (target_width, target_height))
439
 
440
+ print(f"Resizing video to {target_width}x{target_height}...")
441
  frame_count = 0
442
 
443
  while True:
 
453
  cap.release()
454
  out.release()
455
 
456
+ print(f"Video resized successfully ({frame_count} frames)")
457
  return temp_output.name
458
 
459
  except Exception as e:
460
+ print(f"Video resize failed: {str(e)}")
461
  return input_path # Return original if resize fails
462
 
463
  def get_video_info(video_path):
 
512
  </style>
513
  <div style="text-align: center; color: white;">
514
  <div style="font-size: 24px; font-weight: bold; margin-bottom: 10px;">
515
+ Processing Your Recording...
516
  </div>
517
  <div style="font-size: 16px; opacity: 0.95;">
518
  Saving video file • Preparing for download
 
546
  <div style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); padding: 15px; border-radius: 10px; margin: 20px 0;">
547
  <div style="text-align: center; color: white;">
548
  <div style="font-size: 20px; font-weight: bold;">
549
+ Video Saved Successfully!
550
  </div>
551
  <div style="font-size: 14px; margin-top: 5px; opacity: 0.95;">
552
  Ready for download • Click "Analyze Video" to assess
 
555
  </div>
556
  """
557
 
558
+ print(f"Video saved: {output_filename}")
559
  return temp_output.name, gr.update(value=success_html, visible=True)
560
 
561
  except Exception as e:
562
+ print(f"Failed to save video: {str(e)}")
563
  error_html = """
564
  <div style="background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); padding: 15px; border-radius: 10px; margin: 20px 0;">
565
  <div style="text-align: center; color: white;">
566
  <div style="font-size: 20px; font-weight: bold;">
567
+ Error Saving Video
568
  </div>
569
  <div style="font-size: 14px; margin-top: 5px;">
570
  Please try recording again
 
574
  """
575
  return None, gr.update(value=error_html, visible=True)
576
 
 
 
 
 
 
 
577
  # Define the core processing function (separate from GPU wrapper)
578
+ def process_video_core(video, resize_option, assessor, param1, param2, param3, param4, param5):
579
+ """Process uploaded or recorded video with custom parameters"""
580
 
581
  if video is None:
582
  return "Please upload or record a video first.", None, None, None
 
585
  # Get original video info
586
  orig_width, orig_height, fps, frame_count = get_video_info(video)
587
  if orig_width and orig_height:
588
+ print(f"Original video: {orig_width}x{orig_height} @ {fps}fps ({frame_count} frames)")
589
 
590
  # Get file size
591
  file_size_mb = os.path.getsize(video) / (1024 * 1024)
592
+ print(f"Processing video ({file_size_mb:.1f}MB)...")
593
 
594
  # Apply resizing based on user selection
595
  video_to_process = video
 
613
 
614
  # Check new file size
615
  new_file_size_mb = os.path.getsize(video_to_process) / (1024 * 1024)
616
+ print(f"Resized video: {new_file_size_mb:.1f}MB (saved {file_size_mb - new_file_size_mb:.1f}MB)")
617
 
618
+ # Start assessment with parameters
619
+ print(f"Starting Healthcare Team Assessment...")
620
+ print(f"Parameters: Communication={param1}, Teamwork={param2}, Safety={param3}, Emergency={param4}, Leadership={param5}")
621
 
622
+ assessment_result, p1, p2, p3, p4, p5 = assessor.analyze_video(video_to_process, param1, param2, param3, param4, param5)
623
 
624
  # Clean up temporary resized file if created
625
  if temp_resized_file and temp_resized_file != video:
 
632
  return assessment_result, None, None, None
633
 
634
  # Generate 1-minute audio feedback
635
+ print("Generating 1-minute audio summary...")
636
  audio_path = assessor.generate_audio_feedback(assessment_result)
637
 
638
+ # Generate PDF report with parameters
639
+ print("Generating PDF report...")
640
+ pdf_path = assessor.generate_pdf_report(assessment_result, param1, param2, param3, param4, param5)
641
 
642
  # Parse scores for visual summary
643
+ weighted_score, percentage, level, color = assessor.parse_assessment_scores(assessment_result, param1, param2, param3, param4, param5)
644
 
645
+ # Create enhanced visual summary HTML with parameter information
646
  summary_html = f"""
647
  <div style="max-width:800px; margin:20px auto; padding:30px; border-radius:15px; box-shadow:0 2px 10px rgba(0,0,0,0.08); background:white;">
648
+ <h2 style="text-align:center; color:#111827; margin-bottom:30px; font-weight:600;">Customized Assessment Summary</h2>
649
 
650
  <div style="display:flex; justify-content:space-around; margin:30px 0;">
651
  <div style="text-align:center;">
652
+ <div style="font-size:48px; font-weight:bold; color:{color};">{weighted_score:.1f}/10</div>
653
+ <div style="color:#4B5563; margin-top:10px; font-weight:500;">Weighted Score</div>
654
  </div>
655
  <div style="text-align:center;">
656
  <div style="font-size:48px; font-weight:bold; color:{color};">{percentage:.0f}%</div>
657
+ <div style="color:#4B5563; margin-top:10px; font-weight:500;">Overall Performance</div>
658
  </div>
659
  </div>
660
 
 
663
  </div>
664
 
665
  <div style="margin-top:30px;">
666
+ <h3 style="color:#111827; margin-bottom:20px; font-weight:600;">Your Evaluation Parameters:</h3>
667
+
668
+ <div style="background:#F8FAFC; padding:20px; border-radius:10px; border:1px solid #E2E8F0;">
669
+ <div style="display:flex; justify-content:space-between; margin:10px 0;">
670
+ <span style="color:#374151; font-weight:500;">Communication Effectiveness:</span>
671
+ <span style="color:#111827; font-weight:bold;">{param1}/10</span>
672
+ </div>
673
+ <div style="height:8px; background:#E5E7EB; border-radius:4px; margin:5px 0;">
674
+ <div style="height:100%; background:#3B82F6; border-radius:4px; width:{param1*10}%;"></div>
675
+ </div>
676
+
677
+ <div style="display:flex; justify-content:space-between; margin:10px 0; margin-top:20px;">
678
+ <span style="color:#374151; font-weight:500;">Teamwork & Collaboration:</span>
679
+ <span style="color:#111827; font-weight:bold;">{param2}/10</span>
680
+ </div>
681
+ <div style="height:8px; background:#E5E7EB; border-radius:4px; margin:5px 0;">
682
+ <div style="height:100%; background:#10B981; border-radius:4px; width:{param2*10}%;"></div>
683
+ </div>
684
+
685
+ <div style="display:flex; justify-content:space-between; margin:10px 0; margin-top:20px;">
686
+ <span style="color:#374151; font-weight:500;">Safety Protocols:</span>
687
+ <span style="color:#111827; font-weight:bold;">{param3}/10</span>
688
+ </div>
689
+ <div style="height:8px; background:#E5E7EB; border-radius:4px; margin:5px 0;">
690
+ <div style="height:100%; background:#F59E0B; border-radius:4px; width:{param3*10}%;"></div>
691
+ </div>
692
+
693
+ <div style="display:flex; justify-content:space-between; margin:10px 0; margin-top:20px;">
694
+ <span style="color:#374151; font-weight:500;">Emergency Response:</span>
695
+ <span style="color:#111827; font-weight:bold;">{param4}/10</span>
696
+ </div>
697
+ <div style="height:8px; background:#E5E7EB; border-radius:4px; margin:5px 0;">
698
+ <div style="height:100%; background:#EF4444; border-radius:4px; width:{param4*10}%;"></div>
699
+ </div>
700
+
701
+ <div style="display:flex; justify-content:space-between; margin:10px 0; margin-top:20px;">
702
+ <span style="color:#374151; font-weight:500;">Leadership & Roles:</span>
703
+ <span style="color:#111827; font-weight:bold;">{param5}/10</span>
704
+ </div>
705
+ <div style="height:8px; background:#E5E7EB; border-radius:4px; margin:5px 0;">
706
+ <div style="height:100%; background:#60A5FA; border-radius:4px; width:{param5*10}%;"></div>
707
+ </div>
708
  </div>
709
+ </div>
710
 
711
+ <div style="margin-top:30px;">
712
+ <h3 style="color:#111827; margin-bottom:20px; font-weight:600;">Key Assessment Areas:</h3>
713
+
714
+ <div style="background:#F8FAFC; padding:15px; border-radius:10px; margin:10px 0; border:1px solid #E2E8F0;">
715
+ <p style="color:#374151; line-height:1.8;">
716
+ Your assessment focused on the parameters you prioritized. Areas with higher weights
717
+ received more detailed evaluation and have greater impact on the final score.
718
+ </p>
719
  </div>
720
  </div>
721
 
722
  <div style="margin-top:30px; padding:20px; background:#FFF7ED; border-radius:10px; border-left:4px solid #EA580C;">
723
  <p style="text-align:center; color:#431407; margin:0; font-weight:600;">
724
+ Listen to the 1-minute audio summary for key findings<br>
725
+ Download the PDF report for complete documentation
726
  </p>
727
  </div>
728
  </div>
 
731
  return assessment_result, summary_html, audio_path, pdf_path
732
 
733
  except Exception as e:
734
+ error_msg = f"Error during processing: {str(e)}"
735
  print(error_msg)
736
  return error_msg, None, None, None
737
 
738
  # Wrapper function that calls the GPU-accelerated version
739
+ def process_video(video, resize_option, param1, param2, param3, param4, param5):
740
+ """Wrapper function to call GPU-accelerated processing with parameters"""
741
+ return process_video_with_gpu(video, resize_option, param1, param2, param3, param4, param5)
742
 
743
+ # Create and launch the Gradio interface with parameter controls
744
+ print("Launching Healthcare Assessment Tool with Custom Parameters...")
745
 
746
+ with gr.Blocks(title="Healthcare Team Assessment Tool", theme=gr.themes.Soft()) as demo:
747
 
748
  gr.Markdown("""
749
+ # Healthcare Team Assessment Tool
750
+
751
+ **Customize your video evaluation with 5 key parameters to focus on what matters most to your team**
752
 
753
+ This tool allows you to adjust the evaluation weights for different aspects of healthcare team performance.
754
+ Set higher values for areas you want to prioritize in the assessment.
 
 
 
755
 
756
  ---
757
  """)
758
 
759
  with gr.Row():
760
  with gr.Column(scale=1):
761
+ gr.Markdown("### Video Input")
762
 
763
  # Video resolution dropdown
764
  resize_dropdown = gr.Dropdown(
 
780
  include_audio=True,
781
  interactive=True,
782
  webcam_constraints={"video": {"width": 800, "height": 600}},
783
+ autoplay=False,
784
+ show_download_button=True
785
  )
786
 
787
  # Status bar for immediate feedback
 
793
 
794
  # Add download component for recorded videos
795
  recorded_video_download = gr.File(
796
+ label="Download Recorded Video",
797
  interactive=False,
798
  visible=False
799
  )
800
+
801
+ gr.Markdown("### Evaluation Parameters")
802
+ gr.Markdown("**Set the importance (0-10) for each assessment area:**")
803
+
804
+ # Add the 5 parameter sliders
805
+ param1_slider = gr.Slider(
806
+ minimum=0,
807
+ maximum=10,
808
+ value=8,
809
+ step=1,
810
+ label="Communication Effectiveness",
811
+ info="Clear verbal communication, active listening, information sharing"
812
+ )
813
+
814
+ param2_slider = gr.Slider(
815
+ minimum=0,
816
+ maximum=10,
817
+ value=7,
818
+ step=1,
819
+ label="Teamwork & Collaboration",
820
+ info="Team coordination, mutual support, collaborative problem-solving"
821
+ )
822
+
823
+ param3_slider = gr.Slider(
824
+ minimum=0,
825
+ maximum=10,
826
+ value=6,
827
+ step=1,
828
+ label="Safety Protocols",
829
+ info="Patient safety measures, infection control, risk identification"
830
+ )
831
+
832
+ param4_slider = gr.Slider(
833
+ minimum=0,
834
+ maximum=10,
835
+ value=9,
836
+ step=1,
837
+ label="Emergency Response",
838
+ info="Response time, priority setting, critical intervention timing"
839
+ )
840
+
841
+ param5_slider = gr.Slider(
842
+ minimum=0,
843
+ maximum=10,
844
+ value=5,
845
+ step=1,
846
+ label="Leadership & Roles",
847
+ info="Role assignments, leadership presence, delegation effectiveness"
848
+ )
849
 
850
  gr.Markdown("""
851
+ ### Instructions:
852
+ 1. **Set your evaluation parameters** (higher = more important)
853
+ 2. **Select video resolution** (lower = faster)
854
+ 3. **Upload** or **Record** a video
855
+ 4. Click **Analyze Video** to start assessment
856
+ 5. Review results weighted by your priorities
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857
  """)
858
 
859
  with gr.Column(scale=2):
860
+ gr.Markdown("### Assessment Results")
861
 
862
+ # Move analyze button here
863
+ analyze_btn = gr.Button("Analyze Video", variant="primary", size="lg")
864
 
865
  # Visual summary
866
  summary_output = gr.HTML(
 
870
 
871
  # Audio feedback - downloadable
872
  audio_output = gr.Audio(
873
+ label="1-Minute Audio Summary (Downloadable)",
874
  type="filepath",
875
  interactive=False
876
  )
877
 
878
  # PDF report - downloadable
879
  pdf_output = gr.File(
880
+ label="Download Full PDF Report",
881
  interactive=False,
882
  file_types=[".pdf", ".txt"]
883
  )
884
 
885
  # Detailed assessment text
886
  assessment_output = gr.Textbox(
887
+ label="Detailed Assessment (Text View)",
888
  lines=20,
889
  max_lines=30,
890
  interactive=False,
 
894
  # Footer
895
  gr.Markdown("""
896
  ---
897
+ ### About Customizable Assessment
898
+ This tool uses Google's Gemini AI to evaluate healthcare team performance based on YOUR priorities.
899
+
900
+ **How Parameters Work:**
901
+ - Higher values (8-10) = Critical importance in evaluation
902
+ - Medium values (4-7) = Moderate importance
903
+ - Lower values (0-3) = Less emphasis in assessment
904
+ - Set to 0 to exclude from evaluation
905
+
906
+ The final score is weighted based on your parameter settings, ensuring the assessment
907
+ focuses on what matters most to your training objectives.
908
 
909
  **Powered by Google Gemini 2.0 Flash | ZeroGPU on HuggingFace Spaces**
910
  """)
911
 
912
+ # Auto-save video when recording stops
913
  video_input.stop_recording(
914
  fn=show_saving_status,
915
  inputs=[video_input],
 
934
  outputs=[status_bar]
935
  )
936
 
937
+ # Connect the analyze button with all parameters
938
  analyze_btn.click(
939
  fn=process_video,
940
+ inputs=[
941
+ video_input,
942
+ resize_dropdown,
943
+ param1_slider,
944
+ param2_slider,
945
+ param3_slider,
946
+ param4_slider,
947
+ param5_slider
948
+ ],
949
  outputs=[assessment_output, summary_output, audio_output, pdf_output],
950
  api_name="analyze"
951
  )