DocUA commited on
Commit
bc676df
Β·
1 Parent(s): 1906fa8

feat: enhance verification interface with message review and feedback functionality

Browse files

- Introduce a new message review section for displaying patient messages and classification results
- Add feedback buttons for users to confirm or correct classifications
- Implement correction section for users to submit corrections with optional notes
- Update session statistics display to reflect progress and accuracy
- Bind new event handlers for feedback and correction actions to improve user interaction

src/interface/enhanced_verification_interface.py CHANGED
@@ -238,7 +238,7 @@ class EnhancedVerificationInterface:
238
  label="Dataset Details"
239
  )
240
 
241
- # Verification interface (initially hidden)
242
  verification_section = gr.Row(visible=False)
243
  with verification_section:
244
  with gr.Column():
@@ -266,12 +266,74 @@ class EnhancedVerificationInterface:
266
  label="Progress"
267
  )
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  # Status message for dataset interface
270
  dataset_status_message = gr.Markdown("", visible=True)
271
 
272
  # Dataset state
273
  current_dataset_state = gr.State(value=None)
274
  verification_session_state = gr.State(value=None)
 
 
275
 
276
  manual_input_interface = gr.Row(visible=False)
277
  with manual_input_interface:
@@ -755,29 +817,256 @@ class EnhancedVerificationInterface:
755
  if not current_dataset:
756
  return (
757
  None, # verification_session_state
 
 
 
 
 
 
 
758
  "❌ No dataset selected" # dataset_status_message
759
  )
760
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
  success, message, session = dataset_controller.start_verification_session(
762
  current_dataset, verifier_name
763
  )
764
 
765
  if success:
766
- return (
767
- session, # verification_session_state
768
- message # dataset_status_message
769
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
770
  else:
771
  return (
772
- None, # verification_session_state
773
- message # dataset_status_message
 
 
 
 
 
 
 
774
  )
775
  except Exception as e:
776
  return (
777
  None, # verification_session_state
 
 
 
 
 
 
 
778
  f"❌ Error starting verification: {str(e)}" # dataset_status_message
779
  )
780
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  # Bind dataset interface events
782
  dataset_selector.change(
783
  on_dataset_selection_change,
@@ -794,7 +1083,70 @@ class EnhancedVerificationInterface:
794
  start_verification_btn.click(
795
  on_start_verification,
796
  inputs=[current_dataset_state, verifier_name_input],
797
- outputs=[verification_session_state, dataset_status_message]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
798
  )
799
 
800
  return interface
 
238
  label="Dataset Details"
239
  )
240
 
241
+ # Verification setup section (initially hidden)
242
  verification_section = gr.Row(visible=False)
243
  with verification_section:
244
  with gr.Column():
 
266
  label="Progress"
267
  )
268
 
269
+ # Message review section (initially hidden)
270
+ message_review_section = gr.Row(visible=False)
271
+ with message_review_section:
272
+ with gr.Column(scale=2):
273
+ gr.Markdown("## πŸ“ Message Review")
274
+
275
+ # Current message display
276
+ current_message_display = gr.Textbox(
277
+ label="Patient Message",
278
+ interactive=False,
279
+ lines=4
280
+ )
281
+
282
+ # Classification result
283
+ with gr.Row():
284
+ classifier_decision_display = gr.Markdown(
285
+ "πŸ”„ Loading...",
286
+ label="Classifier Decision"
287
+ )
288
+ classifier_confidence_display = gr.Markdown(
289
+ "",
290
+ label="Confidence"
291
+ )
292
+
293
+ # Feedback buttons
294
+ gr.Markdown("### Is this classification correct?")
295
+ with gr.Row():
296
+ correct_btn = gr.Button("βœ“ Correct", variant="primary", scale=1)
297
+ incorrect_btn = gr.Button("βœ— Incorrect", variant="stop", scale=1)
298
+
299
+ # Correction section (initially hidden)
300
+ correction_section = gr.Row(visible=False)
301
+ with correction_section:
302
+ with gr.Column():
303
+ gr.Markdown("### Select correct classification:")
304
+ correction_selector = gr.Radio(
305
+ choices=["green", "yellow", "red"],
306
+ label="Correct Classification",
307
+ value=None
308
+ )
309
+ correction_notes = gr.Textbox(
310
+ label="Notes (Optional)",
311
+ placeholder="Why is this incorrect?",
312
+ lines=2
313
+ )
314
+ with gr.Row():
315
+ submit_correction_btn = gr.Button("βœ“ Submit Correction", variant="primary")
316
+ cancel_correction_btn = gr.Button("βœ— Cancel", variant="secondary")
317
+
318
+ with gr.Column(scale=1):
319
+ gr.Markdown("## πŸ“Š Session Statistics")
320
+ session_stats_display = gr.Markdown(
321
+ "**Progress:** 0/0\n**Correct:** 0\n**Incorrect:** 0\n**Accuracy:** 0%"
322
+ )
323
+
324
+ # Navigation
325
+ with gr.Row():
326
+ skip_btn = gr.Button("⏭️ Skip", variant="secondary")
327
+ finish_btn = gr.Button("🏁 Finish Session", variant="primary")
328
+
329
  # Status message for dataset interface
330
  dataset_status_message = gr.Markdown("", visible=True)
331
 
332
  # Dataset state
333
  current_dataset_state = gr.State(value=None)
334
  verification_session_state = gr.State(value=None)
335
+ current_message_index_state = gr.State(value=0)
336
+ current_classification_state = gr.State(value=None)
337
 
338
  manual_input_interface = gr.Row(visible=False)
339
  with manual_input_interface:
 
817
  if not current_dataset:
818
  return (
819
  None, # verification_session_state
820
+ gr.Row(visible=False), # message_review_section
821
+ "", # current_message_display
822
+ "", # classifier_decision_display
823
+ "", # classifier_confidence_display
824
+ "", # session_stats_display
825
+ 0, # current_message_index_state
826
+ None, # current_classification_state
827
  "❌ No dataset selected" # dataset_status_message
828
  )
829
 
830
+ if not verifier_name or not verifier_name.strip():
831
+ return (
832
+ None, # verification_session_state
833
+ gr.Row(visible=False), # message_review_section
834
+ "", # current_message_display
835
+ "", # classifier_decision_display
836
+ "", # classifier_confidence_display
837
+ "", # session_stats_display
838
+ 0, # current_message_index_state
839
+ None, # current_classification_state
840
+ "❌ Please enter your name" # dataset_status_message
841
+ )
842
+
843
  success, message, session = dataset_controller.start_verification_session(
844
  current_dataset, verifier_name
845
  )
846
 
847
  if success:
848
+ # Get first message for verification
849
+ first_message, classification_result = dataset_controller.get_current_message_for_verification()
850
+
851
+ if first_message:
852
+ # Format classification display
853
+ decision = classification_result.get('decision', 'unknown').upper()
854
+ confidence = classification_result.get('confidence', 0) * 100
855
+
856
+ decision_colors = {'GREEN': '🟒', 'YELLOW': '🟑', 'RED': 'πŸ”΄'}
857
+ decision_badge = f"{decision_colors.get(decision, '❓')} **{decision}**"
858
+ confidence_text = f"Confidence: **{confidence:.1f}%**"
859
+
860
+ stats_text = f"""**Progress:** 1/{len(current_dataset.messages)}
861
+ **Correct:** 0
862
+ **Incorrect:** 0
863
+ **Accuracy:** N/A"""
864
+
865
+ return (
866
+ session, # verification_session_state
867
+ gr.Row(visible=True), # message_review_section
868
+ first_message.text, # current_message_display
869
+ decision_badge, # classifier_decision_display
870
+ confidence_text, # classifier_confidence_display
871
+ stats_text, # session_stats_display
872
+ 0, # current_message_index_state
873
+ classification_result, # current_classification_state
874
+ message # dataset_status_message
875
+ )
876
+ else:
877
+ return (
878
+ session, # verification_session_state
879
+ gr.Row(visible=False), # message_review_section
880
+ "", # current_message_display
881
+ "", # classifier_decision_display
882
+ "", # classifier_confidence_display
883
+ "", # session_stats_display
884
+ 0, # current_message_index_state
885
+ None, # current_classification_state
886
+ "❌ No messages in dataset" # dataset_status_message
887
+ )
888
  else:
889
  return (
890
+ None, # verification_session_state
891
+ gr.Row(visible=False), # message_review_section
892
+ "", # current_message_display
893
+ "", # classifier_decision_display
894
+ "", # classifier_confidence_display
895
+ "", # session_stats_display
896
+ 0, # current_message_index_state
897
+ None, # current_classification_state
898
+ message # dataset_status_message
899
  )
900
  except Exception as e:
901
  return (
902
  None, # verification_session_state
903
+ gr.Row(visible=False), # message_review_section
904
+ "", # current_message_display
905
+ "", # classifier_decision_display
906
+ "", # classifier_confidence_display
907
+ "", # session_stats_display
908
+ 0, # current_message_index_state
909
+ None, # current_classification_state
910
  f"❌ Error starting verification: {str(e)}" # dataset_status_message
911
  )
912
 
913
+ def on_correct_feedback(current_dataset, session, msg_index, classification):
914
+ """Handle correct classification feedback."""
915
+ try:
916
+ success, message, stats = dataset_controller.submit_verification_feedback(True)
917
+
918
+ if success and not stats.get('is_complete', False):
919
+ # Get next message
920
+ next_message, next_classification = dataset_controller.get_current_message_for_verification()
921
+
922
+ if next_message:
923
+ decision = next_classification.get('decision', 'unknown').upper()
924
+ confidence = next_classification.get('confidence', 0) * 100
925
+
926
+ decision_colors = {'GREEN': '🟒', 'YELLOW': '🟑', 'RED': 'πŸ”΄'}
927
+ decision_badge = f"{decision_colors.get(decision, '❓')} **{decision}**"
928
+ confidence_text = f"Confidence: **{confidence:.1f}%**"
929
+
930
+ stats_text = f"""**Progress:** {stats['processed'] + 1}/{stats['total']}
931
+ **Correct:** {stats['correct']}
932
+ **Incorrect:** {stats['incorrect']}
933
+ **Accuracy:** {stats['accuracy']:.1f}%"""
934
+
935
+ return (
936
+ next_message.text, # current_message_display
937
+ decision_badge, # classifier_decision_display
938
+ confidence_text, # classifier_confidence_display
939
+ stats_text, # session_stats_display
940
+ msg_index + 1, # current_message_index_state
941
+ next_classification, # current_classification_state
942
+ gr.Row(visible=False), # correction_section
943
+ message # dataset_status_message
944
+ )
945
+
946
+ # Session complete
947
+ stats_text = f"""**πŸŽ‰ Session Complete!**
948
+ **Total:** {stats['processed']}
949
+ **Correct:** {stats['correct']}
950
+ **Incorrect:** {stats['incorrect']}
951
+ **Final Accuracy:** {stats['accuracy']:.1f}%"""
952
+
953
+ return (
954
+ "βœ… Verification session complete!", # current_message_display
955
+ "πŸŽ‰ **COMPLETE**", # classifier_decision_display
956
+ "", # classifier_confidence_display
957
+ stats_text, # session_stats_display
958
+ msg_index, # current_message_index_state
959
+ None, # current_classification_state
960
+ gr.Row(visible=False), # correction_section
961
+ f"βœ… Session complete! Accuracy: {stats['accuracy']:.1f}%" # dataset_status_message
962
+ )
963
+ except Exception as e:
964
+ return (
965
+ gr.update(), # current_message_display
966
+ gr.update(), # classifier_decision_display
967
+ gr.update(), # classifier_confidence_display
968
+ gr.update(), # session_stats_display
969
+ msg_index, # current_message_index_state
970
+ classification, # current_classification_state
971
+ gr.Row(visible=False), # correction_section
972
+ f"❌ Error: {str(e)}" # dataset_status_message
973
+ )
974
+
975
+ def on_incorrect_feedback():
976
+ """Show correction section."""
977
+ return (
978
+ gr.Row(visible=True), # correction_section
979
+ "⚠️ Please select the correct classification" # dataset_status_message
980
+ )
981
+
982
+ def on_cancel_correction():
983
+ """Cancel correction."""
984
+ return (
985
+ gr.Row(visible=False), # correction_section
986
+ "❌ Correction cancelled" # dataset_status_message
987
+ )
988
+
989
+ def on_submit_correction(current_dataset, session, msg_index, classification, correction, notes):
990
+ """Submit correction feedback."""
991
+ try:
992
+ if not correction:
993
+ return (
994
+ gr.update(), # current_message_display
995
+ gr.update(), # classifier_decision_display
996
+ gr.update(), # classifier_confidence_display
997
+ gr.update(), # session_stats_display
998
+ msg_index, # current_message_index_state
999
+ classification, # current_classification_state
1000
+ gr.Row(visible=True), # correction_section
1001
+ "❌ Please select a classification" # dataset_status_message
1002
+ )
1003
+
1004
+ success, message, stats = dataset_controller.submit_verification_feedback(False, correction, notes)
1005
+
1006
+ if success and not stats.get('is_complete', False):
1007
+ # Get next message
1008
+ next_message, next_classification = dataset_controller.get_current_message_for_verification()
1009
+
1010
+ if next_message:
1011
+ decision = next_classification.get('decision', 'unknown').upper()
1012
+ confidence = next_classification.get('confidence', 0) * 100
1013
+
1014
+ decision_colors = {'GREEN': '🟒', 'YELLOW': '🟑', 'RED': 'πŸ”΄'}
1015
+ decision_badge = f"{decision_colors.get(decision, '❓')} **{decision}**"
1016
+ confidence_text = f"Confidence: **{confidence:.1f}%**"
1017
+
1018
+ stats_text = f"""**Progress:** {stats['processed'] + 1}/{stats['total']}
1019
+ **Correct:** {stats['correct']}
1020
+ **Incorrect:** {stats['incorrect']}
1021
+ **Accuracy:** {stats['accuracy']:.1f}%"""
1022
+
1023
+ return (
1024
+ next_message.text, # current_message_display
1025
+ decision_badge, # classifier_decision_display
1026
+ confidence_text, # classifier_confidence_display
1027
+ stats_text, # session_stats_display
1028
+ msg_index + 1, # current_message_index_state
1029
+ next_classification, # current_classification_state
1030
+ gr.Row(visible=False), # correction_section
1031
+ message # dataset_status_message
1032
+ )
1033
+
1034
+ # Session complete
1035
+ stats_text = f"""**πŸŽ‰ Session Complete!**
1036
+ **Total:** {stats['processed']}
1037
+ **Correct:** {stats['correct']}
1038
+ **Incorrect:** {stats['incorrect']}
1039
+ **Final Accuracy:** {stats['accuracy']:.1f}%"""
1040
+
1041
+ return (
1042
+ "βœ… Verification session complete!", # current_message_display
1043
+ "πŸŽ‰ **COMPLETE**", # classifier_decision_display
1044
+ "", # classifier_confidence_display
1045
+ stats_text, # session_stats_display
1046
+ msg_index, # current_message_index_state
1047
+ None, # current_classification_state
1048
+ gr.Row(visible=False), # correction_section
1049
+ f"βœ… Session complete! Accuracy: {stats['accuracy']:.1f}%" # dataset_status_message
1050
+ )
1051
+ except Exception as e:
1052
+ return (
1053
+ gr.update(), # current_message_display
1054
+ gr.update(), # classifier_decision_display
1055
+ gr.update(), # classifier_confidence_display
1056
+ gr.update(), # session_stats_display
1057
+ msg_index, # current_message_index_state
1058
+ classification, # current_classification_state
1059
+ gr.Row(visible=True), # correction_section
1060
+ f"❌ Error: {str(e)}" # dataset_status_message
1061
+ )
1062
+
1063
+ def on_finish_session(session):
1064
+ """Finish verification session."""
1065
+ return (
1066
+ gr.Row(visible=False), # message_review_section
1067
+ "βœ… Session finished. You can start a new verification or select another dataset." # dataset_status_message
1068
+ )
1069
+
1070
  # Bind dataset interface events
1071
  dataset_selector.change(
1072
  on_dataset_selection_change,
 
1083
  start_verification_btn.click(
1084
  on_start_verification,
1085
  inputs=[current_dataset_state, verifier_name_input],
1086
+ outputs=[
1087
+ verification_session_state,
1088
+ message_review_section,
1089
+ current_message_display,
1090
+ classifier_decision_display,
1091
+ classifier_confidence_display,
1092
+ session_stats_display,
1093
+ current_message_index_state,
1094
+ current_classification_state,
1095
+ dataset_status_message
1096
+ ]
1097
+ )
1098
+
1099
+ correct_btn.click(
1100
+ on_correct_feedback,
1101
+ inputs=[current_dataset_state, verification_session_state, current_message_index_state, current_classification_state],
1102
+ outputs=[
1103
+ current_message_display,
1104
+ classifier_decision_display,
1105
+ classifier_confidence_display,
1106
+ session_stats_display,
1107
+ current_message_index_state,
1108
+ current_classification_state,
1109
+ correction_section,
1110
+ dataset_status_message
1111
+ ]
1112
+ )
1113
+
1114
+ incorrect_btn.click(
1115
+ on_incorrect_feedback,
1116
+ outputs=[correction_section, dataset_status_message]
1117
+ )
1118
+
1119
+ cancel_correction_btn.click(
1120
+ on_cancel_correction,
1121
+ outputs=[correction_section, dataset_status_message]
1122
+ )
1123
+
1124
+ submit_correction_btn.click(
1125
+ on_submit_correction,
1126
+ inputs=[
1127
+ current_dataset_state,
1128
+ verification_session_state,
1129
+ current_message_index_state,
1130
+ current_classification_state,
1131
+ correction_selector,
1132
+ correction_notes
1133
+ ],
1134
+ outputs=[
1135
+ current_message_display,
1136
+ classifier_decision_display,
1137
+ classifier_confidence_display,
1138
+ session_stats_display,
1139
+ current_message_index_state,
1140
+ current_classification_state,
1141
+ correction_section,
1142
+ dataset_status_message
1143
+ ]
1144
+ )
1145
+
1146
+ finish_btn.click(
1147
+ on_finish_session,
1148
+ inputs=[verification_session_state],
1149
+ outputs=[message_review_section, dataset_status_message]
1150
  )
1151
 
1152
  return interface