AI-RESEARCHER-2024 commited on
Commit
73ca02d
Β·
verified Β·
1 Parent(s): 1ee4bf4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +283 -186
app.py CHANGED
@@ -7,22 +7,36 @@ import re
7
  from gtts import gTTS
8
  import tempfile
9
  import numpy as np
10
- import cv2
11
-
12
- # Configure API
13
- GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
 
 
 
 
 
 
 
 
14
  if GOOGLE_API_KEY:
15
  genai.configure(api_key=GOOGLE_API_KEY)
16
  else:
17
- print("⚠️ Warning: GOOGLE_API_KEY not found in environment variables")
18
 
19
  class CICE_Assessment:
20
  def __init__(self):
21
- self.model = genai.GenerativeModel("gemini-2.0-flash-exp")
 
 
 
22
 
23
  def analyze_video(self, video_path):
24
  """Analyze video using the 18-point CICE 2.0 assessment with specific behavioral cues"""
25
 
 
 
 
26
  try:
27
  # Upload video to Gemini
28
  video_file = genai.upload_file(path=video_path, display_name="healthcare_interaction")
@@ -41,86 +55,85 @@ class CICE_Assessment:
41
  # ENHANCED PROMPT WITH SPECIFIC BEHAVIORAL CUES
42
  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:
43
 
44
- For EACH competency, clearly state whether it was "OBSERVED" or "NOT OBSERVED" based on these specific behaviors:
45
-
46
- 1. IDENTIFIES FACTORS INFLUENCING HEALTH STATUS
47
- LOOK FOR: Team mentions allergy bracelet, fall-related trauma, multiple injuries, or states airway/breathing/circulation concerns out loud
48
-
49
- 2. IDENTIFIES TEAM GOALS FOR THE PATIENT
50
- LOOK FOR: Team verbalizes goals like: stabilize airway, CPR/AED, give epinephrine, control bleeding, preserve tooth, prepare EMS handoff
51
 
52
- 3. PRIORITIZES GOALS FOCUSED ON IMPROVING HEALTH OUTCOMES
53
- LOOK FOR: CPR/AED prioritized before bleeding/dental injury, EpiPen administered before addressing secondary injuries
 
 
 
54
 
55
- 4. VERBALIZES DISCIPLINE-SPECIFIC ROLE (PRE-BRIEF)
56
- LOOK FOR: Students acknowledge interprofessional communication expectations and scene safety review before scenario begins
57
 
58
- 5. OFFERS TO SEEK GUIDANCE FROM COLLEAGUES
59
- LOOK FOR: Peer-to-peer checks (e.g., dental to dental: confirm tooth storage; nursing to nursing: confirm CPR quality)
60
 
61
- 6. COMMUNICATES ABOUT COST-EFFECTIVE AND TIMELY CARE
62
- LOOK FOR: Team chooses readily available supplies (AED, saline, tourniquet) without delay, states need for rapid EMS transfer
63
 
64
- 7. DIRECTS QUESTIONS TO OTHER HEALTH PROFESSIONALS BASED ON EXPERTISE
65
- LOOK FOR: Asks discipline-specific expertise (e.g., "Dentalβ€”what do we do with the tooth?"), invites pharmacy/medical input on epinephrine use
66
 
67
- 8. AVOIDS DISCIPLINE-SPECIFIC TERMINOLOGY
68
- LOOK FOR: Uses plain language like "no pulse" instead of "asystole"
69
 
70
- 9. EXPLAINS DISCIPLINE-SPECIFIC TERMINOLOGY WHEN NECESSARY
71
- LOOK FOR: Clarifies medical/dental terms for others when necessary
72
 
73
- 10. COMMUNICATES ROLES AND RESPONSIBILITIES CLEARLY
74
- LOOK FOR: Announces assignments out loud: "I'll do compressions," "I'll call 911," "I'll document"
75
 
76
- 11. ENGAGES IN ACTIVE LISTENING
77
- LOOK FOR: Repeats back instructions ("Everyone clear for shock"), pauses to hear teammates' updates
78
 
79
- 12. SOLICITS AND ACKNOWLEDGES PERSPECTIVES
80
- LOOK FOR: Leader asks "Anything else we need to address?", responds to peer input respectfully
81
 
82
- 13. RECOGNIZES APPROPRIATE CONTRIBUTIONS
83
- LOOK FOR: Affirms correct actions verbally ("Good catch on allergy bracelet"), non-verbal acknowledgment (nodding, thumbs up)
84
 
85
- 14. RESPECTFUL OF OTHER TEAM MEMBERS
86
- LOOK FOR: Listens without interrupting, values input across professions
87
 
88
- 15. COLLABORATIVELY WORKS THROUGH INTERPROFESSIONAL CONFLICTS
89
- LOOK FOR: Negotiates intervention priorities (airway vs. bleeding) respectfully
90
 
91
- 16. REFLECTS ON STRENGTHS OF TEAM INTERACTIONS (POST-BRIEF)
92
- LOOK FOR: Notes strong teamwork, communication, or role clarity after the scenario
93
 
94
- 17. REFLECTS ON CHALLENGES OF TEAM INTERACTIONS (POST-BRIEF)
95
- LOOK FOR: Identifies confusion, delays, or role overlap in debriefing
96
 
97
- 18. IDENTIFIES HOW TO IMPROVE TEAM EFFECTIVENESS (POST-BRIEF)
98
- LOOK FOR: Suggests faster role assignment, consistent closed-loop communication, earlier epi use
99
 
100
- STRUCTURE YOUR RESPONSE AS FOLLOWS:
 
101
 
102
- ## OVERALL ASSESSMENT
103
- Brief overview of the team interaction quality.
104
 
105
- ## DETAILED COMPETENCY EVALUATION
106
- For each of the 18 competencies, format as:
107
 
108
- Competency [number]: [name]
109
- Status: [OBSERVED/NOT OBSERVED]
110
- Evidence: [Specific behavioral cue observed or explanation of absence]
 
 
111
 
112
- ## STRENGTHS
113
- Top 3-5 key strengths with specific examples
114
 
115
- ## AREAS FOR IMPROVEMENT
116
- Top 3-5 areas needing work with specific suggestions
117
 
118
- ## AUDIO SUMMARY
119
- [Create a 60-second summary focusing on: overall performance level, top 3 strengths, top 3 areas for improvement, and 2 key recommendations]
120
 
121
- ## FINAL SCORE
122
- Competencies Observed: X/18
123
- Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (50-69%)/Needs Improvement (0-49%)]"""
124
 
125
  response = self.model.generate_content([video_file, prompt])
126
  return response.text
@@ -157,12 +170,12 @@ class CICE_Assessment:
157
  try:
158
  tts = gTTS(text=audio_script, lang='en', slow=False, tld='com')
159
 
160
- # Save to temporary file
161
- with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as tmp_file:
162
- audio_path = tmp_file.name
163
- tts.save(audio_path)
164
 
165
- return audio_path
166
  except Exception as e:
167
  print(f"⚠️ Audio generation failed: {str(e)}")
168
  return None
@@ -222,68 +235,152 @@ class CICE_Assessment:
222
  color = "#dc2626"
223
 
224
  return observed_count, total_competencies, percentage, level, color
225
-
226
- # Initialize the assessment tool
227
- assessor = CICE_Assessment()
228
-
229
- def compress_video(input_path, output_path, target_width=640, target_height=360, target_fps=15, target_bitrate='500k'):
230
- """Compress and resize video to reduce file size and processing time"""
231
 
232
- try:
233
- # Open the video
234
- cap = cv2.VideoCapture(input_path)
235
-
236
- # Get original properties
237
- original_fps = cap.get(cv2.CAP_PROP_FPS)
238
- original_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
239
- original_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
240
-
241
- # Calculate aspect ratio and new dimensions
242
- aspect_ratio = original_width / original_height
243
- if aspect_ratio > target_width / target_height:
244
- new_width = target_width
245
- new_height = int(target_width / aspect_ratio)
246
- else:
247
- new_height = target_height
248
- new_width = int(target_height * aspect_ratio)
249
 
250
- # Set up video writer with compression
251
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
252
- out = cv2.VideoWriter(output_path, fourcc, min(target_fps, original_fps), (new_width, new_height))
253
-
254
- frame_skip = max(1, int(original_fps / target_fps))
255
- frame_count = 0
256
-
257
- while True:
258
- ret, frame = cap.read()
259
- if not ret:
260
- break
261
 
262
- # Skip frames to reduce FPS
263
- if frame_count % frame_skip == 0:
264
- # Resize frame
265
- resized_frame = cv2.resize(frame, (new_width, new_height), interpolation=cv2.INTER_AREA)
266
- out.write(resized_frame)
 
 
 
 
267
 
268
- frame_count += 1
269
-
270
- cap.release()
271
- out.release()
272
-
273
- # Get file sizes for comparison
274
- original_size = os.path.getsize(input_path) / (1024 * 1024) # MB
275
- compressed_size = os.path.getsize(output_path) / (1024 * 1024) # MB
276
-
277
- print(f"βœ… Video compressed: {original_size:.2f}MB β†’ {compressed_size:.2f}MB")
278
- print(f" Resolution: {original_width}x{original_height} β†’ {new_width}x{new_height}")
279
- print(f" FPS: {original_fps:.1f} β†’ {min(target_fps, original_fps):.1f}")
280
-
281
- return output_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  except Exception as e:
284
- print(f"⚠️ Compression failed, using original: {str(e)}")
285
  return input_path
286
 
 
 
 
287
  def process_video(video):
288
  """Process uploaded or recorded video"""
289
 
@@ -291,66 +388,66 @@ def process_video(video):
291
  return "Please upload or record a video first.", None, None, None
292
 
293
  if not GOOGLE_API_KEY:
294
- return "❌ Error: GOOGLE_API_KEY not configured. Please set it in your environment variables.", None, None, None
295
-
296
- progress_messages = []
297
 
298
  try:
299
  # Compress video if needed
300
  file_size_mb = os.path.getsize(video) / (1024 * 1024)
301
 
302
  if file_size_mb > 10: # Compress if larger than 10MB
303
- progress_messages.append(f"πŸ“¦ Compressing video ({file_size_mb:.1f}MB)...")
304
- with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as tmp_file:
305
- compressed_path = tmp_file.name
306
- video = compress_video(video, compressed_path)
307
 
308
  # Start assessment
309
- progress_messages.append("πŸ₯ Starting CICE 2.0 Healthcare Team Assessment...")
310
-
311
- # Analyze video
312
- progress_messages.append("πŸ“€ Uploading video to Gemini AI...")
313
- progress_messages.append("⏳ Processing video (this may take 1-2 minutes)...")
314
 
315
  assessment_result = assessor.analyze_video(video)
316
 
317
  if "Error" in assessment_result:
318
  return assessment_result, None, None, None
319
 
320
- progress_messages.append("βœ… Analysis complete!")
321
 
322
  # Generate 1-minute audio feedback
323
- progress_messages.append("πŸ”Š Generating 1-minute audio summary...")
324
  audio_path = assessor.generate_audio_feedback(assessment_result)
325
 
 
 
 
 
326
  # Parse scores for visual summary
327
  observed, total, percentage, level, color = assessor.parse_assessment_scores(assessment_result)
328
 
329
- # Create enhanced visual summary HTML with behavioral cues
330
  summary_html = f"""
331
- <div style="max-width:800px; margin:20px auto; padding:30px; border-radius:15px; box-shadow:0 4px 6px rgba(0,0,0,0.1);">
332
- <h2 style="text-align:center; color:#1f2937;">CICE 2.0 Assessment Summary</h2>
333
 
334
  <div style="display:flex; justify-content:space-around; margin:30px 0;">
335
  <div style="text-align:center;">
336
  <div style="font-size:48px; font-weight:bold; color:{color};">{observed}/{total}</div>
337
- <div style="color:#6b7280;">Competencies Observed</div>
338
  </div>
339
  <div style="text-align:center;">
340
  <div style="font-size:48px; font-weight:bold; color:{color};">{percentage:.0f}%</div>
341
- <div style="color:#6b7280;">Overall Score</div>
342
  </div>
343
  </div>
344
 
345
- <div style="text-align:center; padding:20px; background:#f9fafb; border-radius:10px;">
346
  <div style="font-size:24px; font-weight:bold; color:{color};">Performance Level: {level}</div>
347
  </div>
348
 
349
  <div style="margin-top:30px;">
350
- <h3>🎯 Key Behavioral Indicators Assessed:</h3>
 
351
  <div style="background:#f3f4f6; padding:15px; border-radius:10px; margin:15px 0;">
352
  <h4 style="color:#059669; margin-top:0;">βœ… Critical Actions</h4>
353
- <ul style="line-height:1.6; color:#374151;">
354
  <li>CPR/AED prioritization</li>
355
  <li>Epinephrine administration timing</li>
356
  <li>Clear role assignments ("I'll do compressions")</li>
@@ -360,7 +457,7 @@ def process_video(video):
360
 
361
  <div style="background:#f3f4f6; padding:15px; border-radius:10px; margin:15px 0;">
362
  <h4 style="color:#0891b2; margin-top:0;">πŸ—£οΈ Communication Markers</h4>
363
- <ul style="line-height:1.6; color:#374151;">
364
  <li>Plain language use (avoiding medical jargon)</li>
365
  <li>Active listening (repeating back instructions)</li>
366
  <li>Soliciting input ("Anything else we need?")</li>
@@ -370,7 +467,7 @@ def process_video(video):
370
 
371
  <div style="background:#f3f4f6; padding:15px; border-radius:10px; margin:15px 0;">
372
  <h4 style="color:#7c3aed; margin-top:0;">πŸ”„ Team Dynamics</h4>
373
- <ul style="line-height:1.6; color:#374151;">
374
  <li>Pre-brief safety review</li>
375
  <li>Peer-to-peer verification</li>
376
  <li>Respectful conflict resolution</li>
@@ -379,28 +476,21 @@ def process_video(video):
379
  </div>
380
  </div>
381
 
382
- <div style="margin-top:20px; padding:15px; background:#fef3c7; border-radius:10px;">
383
- <p style="text-align:center; color:#92400e; margin:0;">
384
- <strong>πŸ”Š Listen to the 1-minute audio summary for key findings and recommendations</strong>
 
385
  </p>
386
  </div>
387
  </div>
388
  """
389
 
390
- # Save assessment to file
391
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
392
- report_filename = f"cice_assessment_{timestamp}.txt"
393
- with open(report_filename, "w") as f:
394
- f.write("CICE 2.0 Healthcare Team Interaction Assessment\n")
395
- f.write("="*60 + "\n")
396
- f.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
397
- f.write("="*60 + "\n\n")
398
- f.write(assessment_result)
399
-
400
- return assessment_result, summary_html, audio_path, report_filename
401
 
402
  except Exception as e:
403
- return f"❌ Error: {str(e)}", None, None, None
 
 
404
 
405
  def create_interface():
406
  """Create the Gradio interface"""
@@ -429,15 +519,7 @@ def create_interface():
429
  sources=["upload", "webcam"],
430
  format="mp4",
431
  include_audio=True,
432
- interactive=True,
433
- webcam_constraints={
434
- "video": {
435
- "width": {"ideal": 640, "max": 640},
436
- "height": {"ideal": 360, "max": 360},
437
- "frameRate": {"ideal": 15, "max": 24}
438
- },
439
- "audio": True
440
- }
441
  )
442
 
443
  analyze_btn = gr.Button("πŸ” Analyze Video", variant="primary", size="lg")
@@ -447,8 +529,8 @@ def create_interface():
447
  1. **Upload** a pre-recorded video or **Record** using your webcam
448
  2. Click **Analyze Video** to start the assessment
449
  3. Wait for the AI to process (1-2 minutes)
450
- 4. Listen to the **1-minute audio summary** for quick insights
451
- 5. Review the detailed written assessment for complete evaluation
452
 
453
  **Key Behaviors Assessed:**
454
  - Allergy/medical history identification
@@ -457,54 +539,69 @@ def create_interface():
457
  - Plain language communication
458
  - Active listening behaviors
459
  - Team respect and conflict resolution
 
 
460
  """)
461
 
462
  with gr.Column(scale=2):
463
  gr.Markdown("### πŸ“Š Assessment Results")
464
 
465
  # Visual summary
466
- summary_output = gr.HTML(label="Visual Summary")
 
 
 
467
 
468
- # Audio feedback - now prominently featured
469
  audio_output = gr.Audio(
470
- label="πŸ”Š 1-Minute Audio Summary (Listen First!)",
471
  type="filepath",
472
  interactive=False
473
  )
474
 
475
- # Detailed assessment
 
 
 
 
 
 
 
476
  assessment_output = gr.Textbox(
477
- label="Detailed CICE 2.0 Assessment (Full Report)",
478
  lines=20,
479
  max_lines=30,
480
- interactive=False
481
- )
482
-
483
- # Download report
484
- report_file = gr.File(
485
- label="πŸ“₯ Download Full Report",
486
- interactive=False
487
  )
488
 
489
  # Footer
490
  gr.Markdown("""
491
  ---
492
  ### About This Assessment
493
- This tool uses AI to identify specific behavioral markers that indicate effective interprofessional collaboration
494
  in healthcare settings. The assessment focuses on observable actions such as:
495
  - Verbal role assignments ("I'll do compressions")
496
  - Recognition phrases ("Good catch on the allergy bracelet")
497
  - Plain language use instead of medical jargon
498
  - Pre-brief and post-brief team discussions
499
 
500
- **Note:** Ensure clear audio capture of team communications for accurate assessment.
 
 
 
 
 
 
 
501
  """)
502
 
503
  # Connect the analyze button
504
  analyze_btn.click(
505
  fn=process_video,
506
  inputs=[video_input],
507
- outputs=[assessment_output, summary_output, audio_output, report_file]
 
508
  )
509
 
510
  return demo
 
7
  from gtts import gTTS
8
  import tempfile
9
  import numpy as np
10
+ from PIL import Image
11
+ from reportlab.lib.pagesizes import letter
12
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
13
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
14
+ from reportlab.lib.units import inch
15
+ from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER
16
+ from reportlab.lib.colors import HexColor
17
+ import subprocess
18
+ import shutil
19
+
20
+ # Configure API - Hugging Face Spaces compatible
21
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", os.getenv("GOOGLE_API_KEY"))
22
  if GOOGLE_API_KEY:
23
  genai.configure(api_key=GOOGLE_API_KEY)
24
  else:
25
+ print("⚠️ Warning: GOOGLE_API_KEY not found. Please set it in Space Settings > Repository secrets")
26
 
27
  class CICE_Assessment:
28
  def __init__(self):
29
+ if GOOGLE_API_KEY:
30
+ self.model = genai.GenerativeModel("gemini-2.0-flash-exp")
31
+ else:
32
+ self.model = None
33
 
34
  def analyze_video(self, video_path):
35
  """Analyze video using the 18-point CICE 2.0 assessment with specific behavioral cues"""
36
 
37
+ if not self.model:
38
+ return "Error: GOOGLE_API_KEY not configured. Please set it in your Hugging Face Space settings."
39
+
40
  try:
41
  # Upload video to Gemini
42
  video_file = genai.upload_file(path=video_path, display_name="healthcare_interaction")
 
55
  # ENHANCED PROMPT WITH SPECIFIC BEHAVIORAL CUES
56
  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:
57
 
58
+ For EACH competency, clearly state whether it was "OBSERVED" or "NOT OBSERVED" based on these specific behaviors:
 
 
 
 
 
 
59
 
60
+ 1. IDENTIFIES FACTORS INFLUENCING HEALTH STATUS
61
+ LOOK FOR: Team mentions allergy bracelet, fall-related trauma, multiple injuries, or states airway/breathing/circulation concerns out loud
62
+
63
+ 2. IDENTIFIES TEAM GOALS FOR THE PATIENT
64
+ LOOK FOR: Team verbalizes goals like: stabilize airway, CPR/AED, give epinephrine, control bleeding, preserve tooth, prepare EMS handoff
65
 
66
+ 3. PRIORITIZES GOALS FOCUSED ON IMPROVING HEALTH OUTCOMES
67
+ LOOK FOR: CPR/AED prioritized before bleeding/dental injury, EpiPen administered before addressing secondary injuries
68
 
69
+ 4. VERBALIZES DISCIPLINE-SPECIFIC ROLE (PRE-BRIEF)
70
+ LOOK FOR: Students acknowledge interprofessional communication expectations and scene safety review before scenario begins
71
 
72
+ 5. OFFERS TO SEEK GUIDANCE FROM COLLEAGUES
73
+ LOOK FOR: Peer-to-peer checks (e.g., dental to dental: confirm tooth storage; nursing to nursing: confirm CPR quality)
74
 
75
+ 6. COMMUNICATES ABOUT COST-EFFECTIVE AND TIMELY CARE
76
+ LOOK FOR: Team chooses readily available supplies (AED, saline, tourniquet) without delay, states need for rapid EMS transfer
77
 
78
+ 7. DIRECTS QUESTIONS TO OTHER HEALTH PROFESSIONALS BASED ON EXPERTISE
79
+ LOOK FOR: Asks discipline-specific expertise (e.g., "Dentalβ€”what do we do with the tooth?"), invites pharmacy/medical input on epinephrine use
80
 
81
+ 8. AVOIDS DISCIPLINE-SPECIFIC TERMINOLOGY
82
+ LOOK FOR: Uses plain language like "no pulse" instead of "asystole"
83
 
84
+ 9. EXPLAINS DISCIPLINE-SPECIFIC TERMINOLOGY WHEN NECESSARY
85
+ LOOK FOR: Clarifies medical/dental terms for others when necessary
86
 
87
+ 10. COMMUNICATES ROLES AND RESPONSIBILITIES CLEARLY
88
+ LOOK FOR: Announces assignments out loud: "I'll do compressions," "I'll call 911," "I'll document"
89
 
90
+ 11. ENGAGES IN ACTIVE LISTENING
91
+ LOOK FOR: Repeats back instructions ("Everyone clear for shock"), pauses to hear teammates' updates
92
 
93
+ 12. SOLICITS AND ACKNOWLEDGES PERSPECTIVES
94
+ LOOK FOR: Leader asks "Anything else we need to address?", responds to peer input respectfully
95
 
96
+ 13. RECOGNIZES APPROPRIATE CONTRIBUTIONS
97
+ LOOK FOR: Affirms correct actions verbally ("Good catch on allergy bracelet"), non-verbal acknowledgment (nodding, thumbs up)
98
 
99
+ 14. RESPECTFUL OF OTHER TEAM MEMBERS
100
+ LOOK FOR: Listens without interrupting, values input across professions
101
 
102
+ 15. COLLABORATIVELY WORKS THROUGH INTERPROFESSIONAL CONFLICTS
103
+ LOOK FOR: Negotiates intervention priorities (airway vs. bleeding) respectfully
104
 
105
+ 16. REFLECTS ON STRENGTHS OF TEAM INTERACTIONS (POST-BRIEF)
106
+ LOOK FOR: Notes strong teamwork, communication, or role clarity after the scenario
107
 
108
+ 17. REFLECTS ON CHALLENGES OF TEAM INTERACTIONS (POST-BRIEF)
109
+ LOOK FOR: Identifies confusion, delays, or role overlap in debriefing
110
 
111
+ 18. IDENTIFIES HOW TO IMPROVE TEAM EFFECTIVENESS (POST-BRIEF)
112
+ LOOK FOR: Suggests faster role assignment, consistent closed-loop communication, earlier epi use
113
 
114
+ STRUCTURE YOUR RESPONSE AS FOLLOWS:
 
115
 
116
+ ## OVERALL ASSESSMENT
117
+ Brief overview of the team interaction quality.
118
 
119
+ ## DETAILED COMPETENCY EVALUATION
120
+ For each of the 18 competencies, format as:
121
+ Competency [number]: [name]
122
+ Status: [OBSERVED/NOT OBSERVED]
123
+ Evidence: [Specific behavioral cue observed or explanation of absence]
124
 
125
+ ## STRENGTHS
126
+ Top 3-5 key strengths with specific examples
127
 
128
+ ## AREAS FOR IMPROVEMENT
129
+ Top 3-5 areas needing work with specific suggestions
130
 
131
+ ## AUDIO SUMMARY
132
+ [Create a 60-second summary focusing on: overall performance level, top 3 strengths, top 3 areas for improvement, and 2 key recommendations]
133
 
134
+ ## FINAL SCORE
135
+ Competencies Observed: X/18
136
+ Overall Performance Level: [Exemplary (85-100%)/Proficient (70-84%)/Developing (50-69%)/Needs Improvement (0-49%)]"""
137
 
138
  response = self.model.generate_content([video_file, prompt])
139
  return response.text
 
170
  try:
171
  tts = gTTS(text=audio_script, lang='en', slow=False, tld='com')
172
 
173
+ # Create a proper temporary file for HF Spaces
174
+ temp_audio = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3')
175
+ tts.save(temp_audio.name)
176
+ temp_audio.close()
177
 
178
+ return temp_audio.name
179
  except Exception as e:
180
  print(f"⚠️ Audio generation failed: {str(e)}")
181
  return None
 
235
  color = "#dc2626"
236
 
237
  return observed_count, total_competencies, percentage, level, color
 
 
 
 
 
 
238
 
239
+ def generate_pdf_report(self, assessment_text):
240
+ """Generate a PDF report from the assessment text"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
 
242
+ try:
243
+ # Create a temporary file for the PDF
244
+ temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')
 
 
 
 
 
 
 
 
245
 
246
+ # Create the PDF document
247
+ doc = SimpleDocTemplate(
248
+ temp_pdf.name,
249
+ pagesize=letter,
250
+ rightMargin=72,
251
+ leftMargin=72,
252
+ topMargin=72,
253
+ bottomMargin=18,
254
+ )
255
 
256
+ # Container for the 'Flowable' objects
257
+ elements = []
258
+
259
+ # Define styles
260
+ styles = getSampleStyleSheet()
261
+ title_style = ParagraphStyle(
262
+ 'CustomTitle',
263
+ parent=styles['Heading1'],
264
+ fontSize=24,
265
+ textColor=HexColor('#1f2937'),
266
+ spaceAfter=30,
267
+ alignment=TA_CENTER
268
+ )
269
+
270
+ heading_style = ParagraphStyle(
271
+ 'CustomHeading',
272
+ parent=styles['Heading2'],
273
+ fontSize=14,
274
+ textColor=HexColor('#059669'),
275
+ spaceAfter=12,
276
+ spaceBefore=12,
277
+ bold=True
278
+ )
279
+
280
+ body_style = ParagraphStyle(
281
+ 'CustomBody',
282
+ parent=styles['BodyText'],
283
+ fontSize=11,
284
+ alignment=TA_JUSTIFY,
285
+ spaceAfter=12
286
+ )
287
+
288
+ # Add title
289
+ elements.append(Paragraph("CICE 2.0 Healthcare Team Assessment Report", title_style))
290
+ elements.append(Spacer(1, 12))
291
+
292
+ # Add timestamp
293
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
294
+ elements.append(Paragraph(f"<b>Assessment Date:</b> {timestamp}", body_style))
295
+ elements.append(Spacer(1, 20))
296
+
297
+ # Process the assessment text into PDF-friendly format
298
+ lines = assessment_text.split('\n')
299
+
300
+ for line in lines:
301
+ line = line.strip()
302
+
303
+ if not line:
304
+ elements.append(Spacer(1, 6))
305
+ elif line.startswith('##'):
306
+ # Major heading
307
+ heading_text = line.replace('##', '').strip()
308
+ elements.append(Paragraph(heading_text, heading_style))
309
+ elif line.startswith('Competency'):
310
+ # Competency item
311
+ elements.append(Paragraph(f"<b>{line}</b>", body_style))
312
+ elif line.startswith('Status:') or line.startswith('Evidence:'):
313
+ # Sub-items
314
+ elements.append(Paragraph(line, body_style))
315
+ else:
316
+ # Regular text
317
+ # Escape special characters for PDF
318
+ line = line.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
319
+ elements.append(Paragraph(line, body_style))
320
+
321
+ # Build PDF
322
+ doc.build(elements)
323
+ temp_pdf.close()
324
+
325
+ return temp_pdf.name
326
+
327
+ except Exception as e:
328
+ print(f"⚠️ PDF generation failed: {str(e)}")
329
+ # Fallback to text file
330
+ temp_txt = tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w')
331
+ temp_txt.write("CICE 2.0 Healthcare Team Interaction Assessment\n")
332
+ temp_txt.write("="*60 + "\n")
333
+ temp_txt.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
334
+ temp_txt.write("="*60 + "\n\n")
335
+ temp_txt.write(assessment_text)
336
+ temp_txt.close()
337
+ return temp_txt.name
338
+
339
+ def compress_video_ffmpeg(input_path, output_path, target_width=640, target_height=360, target_fps=15, target_bitrate='500k'):
340
+ """Compress video using ffmpeg (more reliable for HF Spaces)"""
341
 
342
+ try:
343
+ # Check if ffmpeg is available
344
+ if not shutil.which('ffmpeg'):
345
+ print("⚠️ ffmpeg not found, skipping compression")
346
+ return input_path
347
+
348
+ # Build ffmpeg command
349
+ cmd = [
350
+ 'ffmpeg',
351
+ '-i', input_path,
352
+ '-vf', f'scale={target_width}:{target_height}',
353
+ '-r', str(target_fps),
354
+ '-b:v', target_bitrate,
355
+ '-c:v', 'libx264',
356
+ '-preset', 'fast',
357
+ '-c:a', 'aac',
358
+ '-b:a', '128k',
359
+ '-y', # Overwrite output
360
+ output_path
361
+ ]
362
+
363
+ # Run ffmpeg
364
+ result = subprocess.run(cmd, capture_output=True, text=True)
365
+
366
+ if result.returncode == 0:
367
+ # Get file sizes for comparison
368
+ original_size = os.path.getsize(input_path) / (1024 * 1024) # MB
369
+ compressed_size = os.path.getsize(output_path) / (1024 * 1024) # MB
370
+
371
+ print(f"βœ… Video compressed: {original_size:.2f}MB β†’ {compressed_size:.2f}MB")
372
+ return output_path
373
+ else:
374
+ print(f"⚠️ ffmpeg compression failed: {result.stderr}")
375
+ return input_path
376
+
377
  except Exception as e:
378
+ print(f"⚠️ Compression failed: {str(e)}")
379
  return input_path
380
 
381
+ # Initialize the assessment tool
382
+ assessor = CICE_Assessment()
383
+
384
  def process_video(video):
385
  """Process uploaded or recorded video"""
386
 
 
388
  return "Please upload or record a video first.", None, None, None
389
 
390
  if not GOOGLE_API_KEY:
391
+ return "❌ Error: GOOGLE_API_KEY not configured. Please set it in your Hugging Face Space Settings > Repository secrets.", None, None, None
 
 
392
 
393
  try:
394
  # Compress video if needed
395
  file_size_mb = os.path.getsize(video) / (1024 * 1024)
396
 
397
  if file_size_mb > 10: # Compress if larger than 10MB
398
+ print(f"πŸ“¦ Compressing video ({file_size_mb:.1f}MB)...")
399
+ temp_compressed = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
400
+ video = compress_video_ffmpeg(video, temp_compressed.name)
 
401
 
402
  # Start assessment
403
+ print("πŸ₯ Starting CICE 2.0 Healthcare Team Assessment...")
404
+ print("πŸ“€ Uploading video to Gemini AI...")
405
+ print("⏳ Processing video (this may take 1-2 minutes)...")
 
 
406
 
407
  assessment_result = assessor.analyze_video(video)
408
 
409
  if "Error" in assessment_result:
410
  return assessment_result, None, None, None
411
 
412
+ print("βœ… Analysis complete!")
413
 
414
  # Generate 1-minute audio feedback
415
+ print("πŸ”Š Generating 1-minute audio summary...")
416
  audio_path = assessor.generate_audio_feedback(assessment_result)
417
 
418
+ # Generate PDF report
419
+ print("πŸ“„ Generating PDF report...")
420
+ pdf_path = assessor.generate_pdf_report(assessment_result)
421
+
422
  # Parse scores for visual summary
423
  observed, total, percentage, level, color = assessor.parse_assessment_scores(assessment_result)
424
 
425
+ # Create enhanced visual summary HTML
426
  summary_html = f"""
427
+ <div style="max-width:800px; margin:20px auto; padding:30px; border-radius:15px; box-shadow:0 4px 6px rgba(0,0,0,0.1); background:white;">
428
+ <h2 style="text-align:center; color:#1f2937; margin-bottom:30px;">CICE 2.0 Assessment Summary</h2>
429
 
430
  <div style="display:flex; justify-content:space-around; margin:30px 0;">
431
  <div style="text-align:center;">
432
  <div style="font-size:48px; font-weight:bold; color:{color};">{observed}/{total}</div>
433
+ <div style="color:#6b7280; margin-top:10px;">Competencies Observed</div>
434
  </div>
435
  <div style="text-align:center;">
436
  <div style="font-size:48px; font-weight:bold; color:{color};">{percentage:.0f}%</div>
437
+ <div style="color:#6b7280; margin-top:10px;">Overall Score</div>
438
  </div>
439
  </div>
440
 
441
+ <div style="text-align:center; padding:20px; background:#f9fafb; border-radius:10px; margin:20px 0;">
442
  <div style="font-size:24px; font-weight:bold; color:{color};">Performance Level: {level}</div>
443
  </div>
444
 
445
  <div style="margin-top:30px;">
446
+ <h3 style="color:#1f2937; margin-bottom:20px;">🎯 Key Behavioral Indicators Assessed:</h3>
447
+
448
  <div style="background:#f3f4f6; padding:15px; border-radius:10px; margin:15px 0;">
449
  <h4 style="color:#059669; margin-top:0;">βœ… Critical Actions</h4>
450
+ <ul style="line-height:1.8; color:#374151; margin:10px 0;">
451
  <li>CPR/AED prioritization</li>
452
  <li>Epinephrine administration timing</li>
453
  <li>Clear role assignments ("I'll do compressions")</li>
 
457
 
458
  <div style="background:#f3f4f6; padding:15px; border-radius:10px; margin:15px 0;">
459
  <h4 style="color:#0891b2; margin-top:0;">πŸ—£οΈ Communication Markers</h4>
460
+ <ul style="line-height:1.8; color:#374151; margin:10px 0;">
461
  <li>Plain language use (avoiding medical jargon)</li>
462
  <li>Active listening (repeating back instructions)</li>
463
  <li>Soliciting input ("Anything else we need?")</li>
 
467
 
468
  <div style="background:#f3f4f6; padding:15px; border-radius:10px; margin:15px 0;">
469
  <h4 style="color:#7c3aed; margin-top:0;">πŸ”„ Team Dynamics</h4>
470
+ <ul style="line-height:1.8; color:#374151; margin:10px 0;">
471
  <li>Pre-brief safety review</li>
472
  <li>Peer-to-peer verification</li>
473
  <li>Respectful conflict resolution</li>
 
476
  </div>
477
  </div>
478
 
479
+ <div style="margin-top:30px; padding:20px; background:#fef3c7; border-radius:10px; border-left:4px solid #f59e0b;">
480
+ <p style="text-align:center; color:#92400e; margin:0; font-weight:bold;">
481
+ πŸ”Š Listen to the 1-minute audio summary for key findings<br>
482
+ πŸ“„ Download the PDF report for complete documentation
483
  </p>
484
  </div>
485
  </div>
486
  """
487
 
488
+ return assessment_result, summary_html, audio_path, pdf_path
 
 
 
 
 
 
 
 
 
 
489
 
490
  except Exception as e:
491
+ error_msg = f"❌ Error during processing: {str(e)}"
492
+ print(error_msg)
493
+ return error_msg, None, None, None
494
 
495
  def create_interface():
496
  """Create the Gradio interface"""
 
519
  sources=["upload", "webcam"],
520
  format="mp4",
521
  include_audio=True,
522
+ interactive=True
 
 
 
 
 
 
 
 
523
  )
524
 
525
  analyze_btn = gr.Button("πŸ” Analyze Video", variant="primary", size="lg")
 
529
  1. **Upload** a pre-recorded video or **Record** using your webcam
530
  2. Click **Analyze Video** to start the assessment
531
  3. Wait for the AI to process (1-2 minutes)
532
+ 4. Listen to the **1-minute audio summary**
533
+ 5. Download the **PDF report** for documentation
534
 
535
  **Key Behaviors Assessed:**
536
  - Allergy/medical history identification
 
539
  - Plain language communication
540
  - Active listening behaviors
541
  - Team respect and conflict resolution
542
+
543
+ **Note:** Ensure your GOOGLE_API_KEY is configured in the Space settings.
544
  """)
545
 
546
  with gr.Column(scale=2):
547
  gr.Markdown("### πŸ“Š Assessment Results")
548
 
549
  # Visual summary
550
+ summary_output = gr.HTML(
551
+ label="Visual Summary",
552
+ value="<p style='text-align:center; color:#6b7280; padding:40px;'>Results will appear here after analysis...</p>"
553
+ )
554
 
555
+ # Audio feedback - downloadable
556
  audio_output = gr.Audio(
557
+ label="πŸ”Š 1-Minute Audio Summary (Downloadable)",
558
  type="filepath",
559
  interactive=False
560
  )
561
 
562
+ # PDF report - downloadable
563
+ pdf_output = gr.File(
564
+ label="πŸ“„ Download Full PDF Report",
565
+ interactive=False,
566
+ file_types=[".pdf", ".txt"]
567
+ )
568
+
569
+ # Detailed assessment text
570
  assessment_output = gr.Textbox(
571
+ label="Detailed CICE 2.0 Assessment (Text View)",
572
  lines=20,
573
  max_lines=30,
574
+ interactive=False,
575
+ placeholder="Detailed assessment will appear here..."
 
 
 
 
 
576
  )
577
 
578
  # Footer
579
  gr.Markdown("""
580
  ---
581
  ### About This Assessment
582
+ This tool uses Google's Gemini AI to identify specific behavioral markers that indicate effective interprofessional collaboration
583
  in healthcare settings. The assessment focuses on observable actions such as:
584
  - Verbal role assignments ("I'll do compressions")
585
  - Recognition phrases ("Good catch on the allergy bracelet")
586
  - Plain language use instead of medical jargon
587
  - Pre-brief and post-brief team discussions
588
 
589
+ **Requirements:**
590
+ - Clear audio capture of team communications
591
+ - Video showing team interactions
592
+ - Google API key configured in Space settings
593
+
594
+ **Output Files:**
595
+ - πŸ“Š 1-minute audio summary (MP3 format)
596
+ - πŸ“„ Complete PDF assessment report
597
  """)
598
 
599
  # Connect the analyze button
600
  analyze_btn.click(
601
  fn=process_video,
602
  inputs=[video_input],
603
+ outputs=[assessment_output, summary_output, audio_output, pdf_output],
604
+ api_name="analyze"
605
  )
606
 
607
  return demo