AI-RESEARCHER-2024 commited on
Commit
de286ce
Β·
verified Β·
1 Parent(s): 47a9d9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +210 -61
app.py CHANGED
@@ -7,6 +7,12 @@ import re
7
  from gtts import gTTS
8
  import tempfile
9
  import base64
 
 
 
 
 
 
10
 
11
  # Page configuration
12
  st.set_page_config(
@@ -174,6 +180,45 @@ def parse_assessment_score(assessment_text):
174
  pass
175
  return 0, 0, "Unknown", "#6b7280"
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  def main():
178
  # Header
179
  st.markdown("""
@@ -224,72 +269,126 @@ def main():
224
  col1, col2 = st.columns([2, 1])
225
 
226
  with col1:
227
- st.header("πŸ“Ή Upload Healthcare Team Video")
228
 
229
- uploaded_file = st.file_uploader(
230
- "Choose a video file",
231
- type=['mp4', 'webm', 'avi', 'mov'],
232
- help="Upload a video of healthcare team interaction for CICE 2.0 assessment"
233
- )
234
 
235
- if uploaded_file is not None:
236
- st.success(f"βœ… Video uploaded: {uploaded_file.name}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
- # Display video
239
- st.video(uploaded_file)
 
 
 
 
 
 
 
 
 
 
240
 
241
- # Analyze button
242
- if st.button("πŸ” Analyze with CICE 2.0", type="primary"):
243
- if not api_key:
244
- st.error("❌ Please enter your Google Gemini API key in the sidebar")
 
245
  else:
246
- try:
247
- assessor = CICE_Assessment(api_key)
248
-
249
- with st.spinner("πŸ€– Analyzing video with CICE 2.0 framework... This may take 1-2 minutes"):
250
- # Reset file pointer
251
- uploaded_file.seek(0)
252
- assessment_result = assessor.analyze_video(uploaded_file)
253
-
254
- # Parse score
255
- observed, percentage, level, color = parse_assessment_score(assessment_result)
256
-
257
- # Display summary
258
- st.markdown(f"""
259
- <div class="score-display">
260
- <h2>CICE 2.0 Assessment Results</h2>
261
- <div style="display: flex; justify-content: space-around; margin: 30px 0;">
262
- <div style="text-align: center;">
263
- <div style="font-size: 48px; font-weight: bold; color: {color};">{observed}/18</div>
264
- <div style="color: #6b7280;">Competencies Observed</div>
265
- </div>
266
- <div style="text-align: center;">
267
- <div style="font-size: 48px; font-weight: bold; color: {color};">{percentage:.0f}%</div>
268
- <div style="color: #6b7280;">Overall Score</div>
269
- </div>
270
- </div>
271
- <div style="text-align: center; padding: 20px; background: #f9fafb; border-radius: 10px;">
272
- <div style="font-size: 24px; font-weight: bold; color: {color};">Performance Level: {level}</div>
273
- </div>
274
- </div>
275
- """, unsafe_allow_html=True)
276
-
277
- # Display detailed assessment
278
- st.markdown('<div class="assessment-box">', unsafe_allow_html=True)
279
- st.markdown("### πŸ“‹ Detailed Assessment Report")
280
- st.write(assessment_result)
281
- st.markdown('</div>', unsafe_allow_html=True)
282
-
283
- # Download option
284
- st.download_button(
285
- label="πŸ“₯ Download Assessment Report",
286
- data=assessment_result,
287
- file_name=f"cice_assessment_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
288
- mime="text/plain"
289
- )
290
-
291
- except Exception as e:
292
- st.error(f"❌ Error during assessment: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
  with col2:
295
  st.header("ℹ️ About CICE 2.0")
@@ -316,5 +415,55 @@ def main():
316
  5. Download assessment report
317
  """)
318
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  if __name__ == "__main__":
320
  main()
 
7
  from gtts import gTTS
8
  import tempfile
9
  import base64
10
+ from streamlit_webrtc import webrtc_streamer, WebRtcMode, RTCConfiguration
11
+ import av
12
+ import numpy as np
13
+ import cv2
14
+ import threading
15
+ import queue
16
 
17
  # Page configuration
18
  st.set_page_config(
 
180
  pass
181
  return 0, 0, "Unknown", "#6b7280"
182
 
183
+ # Video recording state
184
+ if "recording" not in st.session_state:
185
+ st.session_state.recording = False
186
+ if "recorded_frames" not in st.session_state:
187
+ st.session_state.recorded_frames = []
188
+ if "frame_queue" not in st.session_state:
189
+ st.session_state.frame_queue = queue.Queue()
190
+
191
+ def video_frame_callback(frame):
192
+ """Callback function to process video frames during recording"""
193
+ if st.session_state.recording:
194
+ img = frame.to_ndarray(format="bgr24")
195
+ st.session_state.frame_queue.put(img)
196
+ return frame
197
+
198
+ def save_recorded_video():
199
+ """Save recorded frames to a video file"""
200
+ if st.session_state.recorded_frames:
201
+ # Create temporary file
202
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
203
+ temp_path = temp_file.name
204
+ temp_file.close()
205
+
206
+ # Video properties
207
+ height, width, _ = st.session_state.recorded_frames[0].shape
208
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
209
+ fps = 20 # frames per second
210
+
211
+ # Create video writer
212
+ out = cv2.VideoWriter(temp_path, fourcc, fps, (width, height))
213
+
214
+ # Write frames
215
+ for frame in st.session_state.recorded_frames:
216
+ out.write(frame)
217
+
218
+ out.release()
219
+ return temp_path
220
+ return None
221
+
222
  def main():
223
  # Header
224
  st.markdown("""
 
269
  col1, col2 = st.columns([2, 1])
270
 
271
  with col1:
272
+ st.header("πŸ“Ή Record or Upload Healthcare Team Video")
273
 
274
+ # Tab selection for recording vs uploading
275
+ tab1, tab2 = st.tabs(["πŸŽ₯ Record Video", "πŸ“ Upload Video"])
 
 
 
276
 
277
+ with tab1:
278
+ st.subheader("Live Video Recording")
279
+
280
+ # WebRTC configuration
281
+ rtc_configuration = RTCConfiguration({
282
+ "iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]
283
+ })
284
+
285
+ # Recording controls
286
+ col_start, col_stop, col_status = st.columns([1, 1, 2])
287
+
288
+ with col_start:
289
+ if st.button("πŸ”΄ Start Recording", disabled=st.session_state.recording):
290
+ st.session_state.recording = True
291
+ st.session_state.recorded_frames = []
292
+ # Clear the queue
293
+ while not st.session_state.frame_queue.empty():
294
+ st.session_state.frame_queue.get()
295
+ st.rerun()
296
 
297
+ with col_stop:
298
+ if st.button("⏹️ Stop Recording", disabled=not st.session_state.recording):
299
+ st.session_state.recording = False
300
+
301
+ # Collect frames from queue
302
+ frames_collected = []
303
+ while not st.session_state.frame_queue.empty():
304
+ frame = st.session_state.frame_queue.get()
305
+ frames_collected.append(frame)
306
+
307
+ st.session_state.recorded_frames = frames_collected
308
+ st.rerun()
309
 
310
+ with col_status:
311
+ if st.session_state.recording:
312
+ st.markdown("πŸ”΄ **Recording...**")
313
+ elif st.session_state.recorded_frames:
314
+ st.markdown(f"βœ… **Recorded {len(st.session_state.recorded_frames)} frames**")
315
  else:
316
+ st.markdown("βšͺ **Ready to record**")
317
+
318
+ # WebRTC streamer
319
+ webrtc_ctx = webrtc_streamer(
320
+ key="video-recorder",
321
+ mode=WebRtcMode.SENDONLY,
322
+ rtc_configuration=rtc_configuration,
323
+ video_frame_callback=video_frame_callback,
324
+ media_stream_constraints={
325
+ "video": {
326
+ "width": {"ideal": 640, "max": 640},
327
+ "height": {"ideal": 480, "max": 480},
328
+ "frameRate": {"ideal": 20, "max": 30}
329
+ },
330
+ "audio": False # Disable audio for now
331
+ },
332
+ async_processing=True,
333
+ )
334
+
335
+ # Process recorded video
336
+ if st.session_state.recorded_frames and not st.session_state.recording:
337
+ st.success(f"βœ… Recording complete! Captured {len(st.session_state.recorded_frames)} frames")
338
+
339
+ if st.button("πŸ’Ύ Save & Analyze Video"):
340
+ with st.spinner("Saving video..."):
341
+ video_path = save_recorded_video()
342
+ if video_path:
343
+ st.success("Video saved successfully!")
344
+
345
+ # Read the saved video for analysis
346
+ with open(video_path, 'rb') as f:
347
+ video_bytes = f.read()
348
+
349
+ # Create a file-like object for analysis
350
+ class VideoFile:
351
+ def __init__(self, data, name):
352
+ self.data = data
353
+ self.name = name
354
+
355
+ def read(self):
356
+ return self.data
357
+
358
+ def seek(self, position):
359
+ pass
360
+
361
+ uploaded_file = VideoFile(video_bytes, "recorded_video.mp4")
362
+
363
+ # Proceed with analysis
364
+ if not api_key:
365
+ st.error("❌ Please enter your Google Gemini API key in the sidebar")
366
+ else:
367
+ analyze_video(uploaded_file, api_key)
368
+
369
+ # Clean up temporary file
370
+ os.unlink(video_path)
371
+
372
+ with tab2:
373
+ st.subheader("Upload Video File")
374
+ uploaded_file = st.file_uploader(
375
+ "Choose a video file",
376
+ type=['mp4', 'webm', 'avi', 'mov'],
377
+ help="Upload a video of healthcare team interaction for CICE 2.0 assessment"
378
+ )
379
+
380
+ if uploaded_file is not None:
381
+ st.success(f"βœ… Video uploaded: {uploaded_file.name}")
382
+
383
+ # Display video
384
+ st.video(uploaded_file)
385
+
386
+ # Analyze button
387
+ if st.button("πŸ” Analyze with CICE 2.0", type="primary"):
388
+ if not api_key:
389
+ st.error("❌ Please enter your Google Gemini API key in the sidebar")
390
+ else:
391
+ analyze_video(uploaded_file, api_key)
392
 
393
  with col2:
394
  st.header("ℹ️ About CICE 2.0")
 
415
  5. Download assessment report
416
  """)
417
 
418
+ def analyze_video(uploaded_file, api_key):
419
+ """Analyze uploaded video with CICE 2.0 assessment"""
420
+ try:
421
+ assessor = CICE_Assessment(api_key)
422
+
423
+ with st.spinner("πŸ€– Analyzing video with CICE 2.0 framework... This may take 1-2 minutes"):
424
+ # Reset file pointer
425
+ uploaded_file.seek(0)
426
+ assessment_result = assessor.analyze_video(uploaded_file)
427
+
428
+ # Parse score
429
+ observed, percentage, level, color = parse_assessment_score(assessment_result)
430
+
431
+ # Display summary
432
+ st.markdown(f"""
433
+ <div class="score-display">
434
+ <h2>CICE 2.0 Assessment Results</h2>
435
+ <div style="display: flex; justify-content: space-around; margin: 30px 0;">
436
+ <div style="text-align: center;">
437
+ <div style="font-size: 48px; font-weight: bold; color: {color};">{observed}/18</div>
438
+ <div style="color: #6b7280;">Competencies Observed</div>
439
+ </div>
440
+ <div style="text-align: center;">
441
+ <div style="font-size: 48px; font-weight: bold; color: {color};">{percentage:.0f}%</div>
442
+ <div style="color: #6b7280;">Overall Score</div>
443
+ </div>
444
+ </div>
445
+ <div style="text-align: center; padding: 20px; background: #f9fafb; border-radius: 10px;">
446
+ <div style="font-size: 24px; font-weight: bold; color: {color};">Performance Level: {level}</div>
447
+ </div>
448
+ </div>
449
+ """, unsafe_allow_html=True)
450
+
451
+ # Display detailed assessment
452
+ st.markdown('<div class="assessment-box">', unsafe_allow_html=True)
453
+ st.markdown("### πŸ“‹ Detailed Assessment Report")
454
+ st.write(assessment_result)
455
+ st.markdown('</div>', unsafe_allow_html=True)
456
+
457
+ # Download option
458
+ st.download_button(
459
+ label="πŸ“₯ Download Assessment Report",
460
+ data=assessment_result,
461
+ file_name=f"cice_assessment_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
462
+ mime="text/plain"
463
+ )
464
+
465
+ except Exception as e:
466
+ st.error(f"❌ Error during assessment: {str(e)}")
467
+
468
  if __name__ == "__main__":
469
  main()