vargha commited on
Commit
b3a5f93
Β·
1 Parent(s): 8ebff9f

auto-load enabled and progress bar

Browse files
Files changed (1) hide show
  1. components/review_dashboard_page.py +116 -5
components/review_dashboard_page.py CHANGED
@@ -106,6 +106,13 @@ class ReviewDashboardPage:
106
  def register_callbacks(self, login_page, session_state: gr.State, root_blocks: gr.Blocks):
107
  self.header.register_callbacks(login_page, self, session_state)
108
 
 
 
 
 
 
 
 
109
  def update_ui_interactive_state(is_interactive: bool):
110
  updates = []
111
  for elem in self.interactive_ui_elements:
@@ -371,12 +378,81 @@ class ReviewDashboardPage:
371
  current_item["annotated_at"],
372
  current_item["validation_status"],
373
  "", # Placeholder for annotator_name
374
- gr.update(value=None, autoplay=False),
375
  gr.update(visible=rejection_visible, value=rejection_reason),
376
  False, # Reset rejection mode
377
  gr.update(value="❌ Reject") # Reset reject button text
378
  )
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  def navigate_review_fn(items, current_idx, direction):
381
  if not items:
382
  return 0
@@ -609,7 +685,7 @@ class ReviewDashboardPage:
609
  # Audio loading is now manual only via the Load Audio button
610
  # Removed automatic filename.change callback to prevent slow loading during initialization
611
 
612
- # Navigation buttons
613
  for btn, direction in [(self.btn_prev, "prev"), (self.btn_next, "next")]:
614
  btn.click(
615
  fn=lambda: update_ui_interactive_state(False),
@@ -622,6 +698,11 @@ class ReviewDashboardPage:
622
  fn=show_current_review_item_fn,
623
  inputs=[self.items_state, self.idx_state, session_state],
624
  outputs=review_display_outputs
 
 
 
 
 
625
  ).then(
626
  lambda: gr.update(value=None),
627
  outputs=self.jump_data_id_input
@@ -630,7 +711,7 @@ class ReviewDashboardPage:
630
  outputs=self.interactive_ui_elements
631
  )
632
 
633
- # Approve/Reject buttons
634
  self.btn_approve.click(
635
  fn=lambda items, idx, session: save_validation_fn(items, idx, session, approved=True, rejection_reason=""), # Pass empty rejection_reason
636
  inputs=[self.items_state, self.idx_state, session_state],
@@ -641,6 +722,11 @@ class ReviewDashboardPage:
641
  ).then(
642
  fn=lambda: gr.update(value="❌ Reject"), # Reset reject button
643
  outputs=[self.btn_reject]
 
 
 
 
 
644
  ).then(
645
  fn=lambda items, idx: navigate_review_fn(items, idx, "next"),
646
  inputs=[self.items_state, self.idx_state],
@@ -649,12 +735,22 @@ class ReviewDashboardPage:
649
  fn=show_current_review_item_fn,
650
  inputs=[self.items_state, self.idx_state, session_state],
651
  outputs=review_display_outputs
 
 
 
 
 
652
  )
653
 
654
  self.btn_reject.click(
655
  fn=handle_rejection_fn,
656
  inputs=[self.items_state, self.idx_state, session_state, self.rejection_reason_input, self.rejection_mode_active],
657
  outputs=[self.items_state, self.current_validation_status, self.rejection_reason_input, self.rejection_mode_active, self.btn_reject]
 
 
 
 
 
658
  ).then(
659
  fn=lambda items, idx, rejection_mode: navigate_review_fn(items, idx, "next") if not rejection_mode else idx,
660
  inputs=[self.items_state, self.idx_state, self.rejection_mode_active],
@@ -675,9 +771,14 @@ class ReviewDashboardPage:
675
  ),
676
  inputs=[self.items_state, self.idx_state, session_state, self.rejection_mode_active],
677
  outputs=review_display_outputs
 
 
 
 
 
678
  )
679
 
680
- # Skip button (just navigate to next)
681
  self.btn_skip.click(
682
  fn=navigate_review_fn,
683
  inputs=[self.items_state, self.idx_state, gr.State("next")],
@@ -686,9 +787,14 @@ class ReviewDashboardPage:
686
  fn=show_current_review_item_fn,
687
  inputs=[self.items_state, self.idx_state, session_state],
688
  outputs=review_display_outputs
 
 
 
 
 
689
  )
690
 
691
- # Jump button
692
  self.btn_jump.click(
693
  fn=jump_by_data_id_fn,
694
  inputs=[self.items_state, self.jump_data_id_input, self.idx_state],
@@ -697,6 +803,11 @@ class ReviewDashboardPage:
697
  fn=show_current_review_item_fn,
698
  inputs=[self.items_state, self.idx_state, session_state],
699
  outputs=review_display_outputs
 
 
 
 
 
700
  ).then(
701
  lambda: gr.update(value=None),
702
  outputs=self.jump_data_id_input
 
106
  def register_callbacks(self, login_page, session_state: gr.State, root_blocks: gr.Blocks):
107
  self.header.register_callbacks(login_page, self, session_state)
108
 
109
+ # Register progress update callback
110
+ self.load_trigger.change(
111
+ fn=get_review_progress_fn,
112
+ inputs=[session_state],
113
+ outputs=self.header.progress_display
114
+ )
115
+
116
  def update_ui_interactive_state(is_interactive: bool):
117
  updates = []
118
  for elem in self.interactive_ui_elements:
 
378
  current_item["annotated_at"],
379
  current_item["validation_status"],
380
  "", # Placeholder for annotator_name
381
+ gr.update(value=None, autoplay=False), # Reset audio (will be loaded manually)
382
  gr.update(visible=rejection_visible, value=rejection_reason),
383
  False, # Reset rejection mode
384
  gr.update(value="❌ Reject") # Reset reject button text
385
  )
386
 
387
+ def get_review_progress_fn(session):
388
+ """Get progress for reviewer showing how many items they've reviewed"""
389
+ user_id = session.get("user_id")
390
+ username = session.get("username")
391
+
392
+ if not user_id or not username:
393
+ return "Review Progress: N/A"
394
+
395
+ # Check if user is a reviewer
396
+ if username not in conf.REVIEW_MAPPING.values():
397
+ return "Review Progress: N/A (Not a reviewer)"
398
+
399
+ # Find which annotator this user should review
400
+ target_annotator = None
401
+ for annotator_name, reviewer_name in conf.REVIEW_MAPPING.items():
402
+ if reviewer_name == username:
403
+ target_annotator = annotator_name
404
+ break
405
+
406
+ if not target_annotator:
407
+ return "Review Progress: N/A (No assignment)"
408
+
409
+ with get_db() as db:
410
+ try:
411
+ # Get target annotator's ID
412
+ target_annotator_obj = db.query(Annotator).filter_by(name=target_annotator).first()
413
+ if not target_annotator_obj:
414
+ return "Review Progress: N/A (Annotator not found)"
415
+
416
+ # Count total annotations by target annotator
417
+ total_annotations = db.query(Annotation).filter(
418
+ Annotation.annotator_id == target_annotator_obj.id
419
+ ).count()
420
+
421
+ # Count reviewed annotations (both approved and rejected)
422
+ reviewed_count = db.query(Validation).filter(
423
+ Validation.validator_id == user_id
424
+ ).join(
425
+ Annotation, Validation.annotation_id == Annotation.id
426
+ ).filter(
427
+ Annotation.annotator_id == target_annotator_obj.id
428
+ ).count()
429
+
430
+ if total_annotations > 0:
431
+ percent = (reviewed_count / total_annotations) * 100
432
+ bar_length = 20 # Length of the progress bar
433
+ filled_length = int(bar_length * reviewed_count // total_annotations)
434
+ bar = 'β–ˆ' * filled_length + 'β–‘' * (bar_length - filled_length)
435
+ return f"Review Progress: {bar} {reviewed_count}/{total_annotations} ({percent:.1f}%)"
436
+ else:
437
+ return "Review Progress: No items to review"
438
+
439
+ except Exception as e:
440
+ log.error(f"Error calculating review progress: {e}")
441
+ return "Review Progress: Error calculating"
442
+
443
+ def auto_load_audio_on_navigate_fn(filename_to_load):
444
+ """Auto-load audio when navigating between items for smooth UX"""
445
+ if not filename_to_load:
446
+ return None, None, gr.update(value=None, autoplay=False)
447
+ try:
448
+ log.info(f"Auto-loading audio for navigation: {filename_to_load}")
449
+ sr, wav = LOADER.load_audio(filename_to_load)
450
+ log.info(f"Auto-loaded audio: {filename_to_load} (SR: {sr}, Length: {len(wav)} samples)")
451
+ return (sr, wav), (sr, wav.copy()), gr.update(value=(sr, wav), autoplay=True)
452
+ except Exception as e:
453
+ log.error(f"Auto audio load failed for {filename_to_load}: {e}")
454
+ return None, None, gr.update(value=None, autoplay=False)
455
+
456
  def navigate_review_fn(items, current_idx, direction):
457
  if not items:
458
  return 0
 
685
  # Audio loading is now manual only via the Load Audio button
686
  # Removed automatic filename.change callback to prevent slow loading during initialization
687
 
688
+ # Navigation buttons with auto-audio loading
689
  for btn, direction in [(self.btn_prev, "prev"), (self.btn_next, "next")]:
690
  btn.click(
691
  fn=lambda: update_ui_interactive_state(False),
 
698
  fn=show_current_review_item_fn,
699
  inputs=[self.items_state, self.idx_state, session_state],
700
  outputs=review_display_outputs
701
+ ).then(
702
+ # Auto-load audio on navigation for smooth UX
703
+ fn=auto_load_audio_on_navigate_fn,
704
+ inputs=[self.filename],
705
+ outputs=[self.audio, self.original_audio_state, self.audio]
706
  ).then(
707
  lambda: gr.update(value=None),
708
  outputs=self.jump_data_id_input
 
711
  outputs=self.interactive_ui_elements
712
  )
713
 
714
+ # Approve/Reject buttons with auto-audio loading and progress updates
715
  self.btn_approve.click(
716
  fn=lambda items, idx, session: save_validation_fn(items, idx, session, approved=True, rejection_reason=""), # Pass empty rejection_reason
717
  inputs=[self.items_state, self.idx_state, session_state],
 
722
  ).then(
723
  fn=lambda: gr.update(value="❌ Reject"), # Reset reject button
724
  outputs=[self.btn_reject]
725
+ ).then(
726
+ # Update progress after approval
727
+ fn=get_review_progress_fn,
728
+ inputs=[session_state],
729
+ outputs=self.header.progress_display
730
  ).then(
731
  fn=lambda items, idx: navigate_review_fn(items, idx, "next"),
732
  inputs=[self.items_state, self.idx_state],
 
735
  fn=show_current_review_item_fn,
736
  inputs=[self.items_state, self.idx_state, session_state],
737
  outputs=review_display_outputs
738
+ ).then(
739
+ # Auto-load audio for next item
740
+ fn=auto_load_audio_on_navigate_fn,
741
+ inputs=[self.filename],
742
+ outputs=[self.audio, self.original_audio_state, self.audio]
743
  )
744
 
745
  self.btn_reject.click(
746
  fn=handle_rejection_fn,
747
  inputs=[self.items_state, self.idx_state, session_state, self.rejection_reason_input, self.rejection_mode_active],
748
  outputs=[self.items_state, self.current_validation_status, self.rejection_reason_input, self.rejection_mode_active, self.btn_reject]
749
+ ).then(
750
+ # Update progress after rejection (only if completed)
751
+ fn=lambda session, rejection_mode: get_review_progress_fn(session) if not rejection_mode else "",
752
+ inputs=[session_state, self.rejection_mode_active],
753
+ outputs=self.header.progress_display
754
  ).then(
755
  fn=lambda items, idx, rejection_mode: navigate_review_fn(items, idx, "next") if not rejection_mode else idx,
756
  inputs=[self.items_state, self.idx_state, self.rejection_mode_active],
 
771
  ),
772
  inputs=[self.items_state, self.idx_state, session_state, self.rejection_mode_active],
773
  outputs=review_display_outputs
774
+ ).then(
775
+ # Auto-load audio for next item (only if completed rejection)
776
+ fn=lambda filename, rejection_mode: auto_load_audio_on_navigate_fn(filename) if not rejection_mode else (None, None, gr.update()),
777
+ inputs=[self.filename, self.rejection_mode_active],
778
+ outputs=[self.audio, self.original_audio_state, self.audio]
779
  )
780
 
781
+ # Skip button (just navigate to next) with auto-audio loading
782
  self.btn_skip.click(
783
  fn=navigate_review_fn,
784
  inputs=[self.items_state, self.idx_state, gr.State("next")],
 
787
  fn=show_current_review_item_fn,
788
  inputs=[self.items_state, self.idx_state, session_state],
789
  outputs=review_display_outputs
790
+ ).then(
791
+ # Auto-load audio for next item
792
+ fn=auto_load_audio_on_navigate_fn,
793
+ inputs=[self.filename],
794
+ outputs=[self.audio, self.original_audio_state, self.audio]
795
  )
796
 
797
+ # Jump button with auto-audio loading
798
  self.btn_jump.click(
799
  fn=jump_by_data_id_fn,
800
  inputs=[self.items_state, self.jump_data_id_input, self.idx_state],
 
803
  fn=show_current_review_item_fn,
804
  inputs=[self.items_state, self.idx_state, session_state],
805
  outputs=review_display_outputs
806
+ ).then(
807
+ # Auto-load audio for jumped item
808
+ fn=auto_load_audio_on_navigate_fn,
809
+ inputs=[self.filename],
810
+ outputs=[self.audio, self.original_audio_state, self.audio]
811
  ).then(
812
  lambda: gr.update(value=None),
813
  outputs=self.jump_data_id_input