Mgolo commited on
Commit
33e6e57
·
verified ·
1 Parent(s): b79d135

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -14
app.py CHANGED
@@ -1,10 +1,9 @@
1
  """
2
  LocaleNLP Translation Service
3
  ============================
4
-
5
  A multi-language translation application supporting English, Wolof, Hausa, and Darija.
6
  Features text, audio, and document translation with automatic chaining for all language pairs.
7
-
8
  Author: LocaleNLP
9
  """
10
 
@@ -16,6 +15,8 @@ from typing import Optional, Dict, Tuple, Any, Union
16
  from pathlib import Path
17
  from dataclasses import dataclass
18
  from enum import Enum
 
 
19
 
20
  import gradio as gr
21
  import torch
@@ -466,6 +467,64 @@ class TranslationApp:
466
 
467
  return ""
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  def create_interface(self) -> gr.Blocks:
470
  """Create and return the Gradio interface."""
471
 
@@ -536,7 +595,36 @@ class TranslationApp:
536
  lines=10,
537
  interactive=False
538
  )
539
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  # Event handlers
541
  def update_visibility(mode: str) -> Dict[str, Any]:
542
  """Update component visibility based on input mode."""
@@ -569,24 +657,61 @@ class TranslationApp:
569
  logger.error(f"Processing error: {e}")
570
  return "", f"❌ Error: {str(e)}"
571
 
572
- def handle_translate(
573
  extracted_text: str,
574
  source_lang: str,
575
  target_lang: str
576
- ) -> str:
577
- """Handle translation of processed text."""
578
  if not extracted_text.strip():
579
- return "📝 No text to translate."
580
  try:
581
- return self.translation_service.translate(
582
  extracted_text,
583
  Language(source_lang),
584
  Language(target_lang)
585
  )
 
586
  except Exception as e:
587
  logger.error(f"Translation error: {e}")
588
- return f"❌ Translation error: {str(e)}"
589
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  # Connect events
591
  input_mode.change(
592
  fn=update_visibility,
@@ -594,14 +719,35 @@ class TranslationApp:
594
  outputs=[input_text, audio_input, file_input, extracted_text, output_text]
595
  )
596
 
597
- translate_btn.click(
 
598
  fn=handle_process,
599
  inputs=[input_mode, input_lang, input_text, audio_input, file_input],
600
  outputs=[extracted_text, output_text]
601
- ).then(
602
- fn=handle_translate,
 
 
603
  inputs=[extracted_text, input_lang, output_lang],
604
- outputs=output_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  )
606
 
607
  return interface
 
1
  """
2
  LocaleNLP Translation Service
3
  ============================
 
4
  A multi-language translation application supporting English, Wolof, Hausa, and Darija.
5
  Features text, audio, and document translation with automatic chaining for all language pairs.
6
+ Includes user feedback logging to CSV.
7
  Author: LocaleNLP
8
  """
9
 
 
15
  from pathlib import Path
16
  from dataclasses import dataclass
17
  from enum import Enum
18
+ import csv
19
+ from datetime import datetime
20
 
21
  import gradio as gr
22
  import torch
 
467
 
468
  return ""
469
 
470
+ def log_feedback(
471
+ self,
472
+ source_lang: Language,
473
+ target_lang: Language,
474
+ user_input: str,
475
+ model_output: str,
476
+ notation: Optional[float] = None,
477
+ correct_translation: Optional[str] = None
478
+ ):
479
+ """
480
+ Log user feedback to a CSV file named '{source}_{target}.csv'.
481
+
482
+ Args:
483
+ source_lang: Source language enum
484
+ target_lang: Target language enum
485
+ user_input: Original input text
486
+ model_output: Model-generated translation
487
+ notation: User rating (optional)
488
+ correct_translation: Corrected version (optional)
489
+ """
490
+ # Define filename
491
+ src = source_lang.value.lower()
492
+ tgt = target_lang.value.lower()
493
+ filename = f"{src}_{tgt}.csv"
494
+
495
+ # Define headers and row
496
+ headers = [
497
+ "timestamp",
498
+ "source_language",
499
+ "target_language",
500
+ "user_input",
501
+ "model_output",
502
+ "notation",
503
+ "correct_translation"
504
+ ]
505
+ row = {
506
+ "timestamp": datetime.now().isoformat(),
507
+ "source_language": source_lang.value,
508
+ "target_language": target_lang.value,
509
+ "user_input": user_input.strip(),
510
+ "model_output": model_output.strip(),
511
+ "notation": notation,
512
+ "correct_translation": correct_translation.strip() if correct_translation else ""
513
+ }
514
+
515
+ # Check if file exists to determine whether to write headers
516
+ file_exists = Path(filename).exists()
517
+
518
+ try:
519
+ with open(filename, mode="a", encoding="utf-8", newline="") as f:
520
+ writer = csv.DictWriter(f, fieldnames=headers)
521
+ if not file_exists:
522
+ writer.writeheader() # Write header only once
523
+ writer.writerow(row)
524
+ logger.info(f"Feedback saved to {filename}")
525
+ except Exception as e:
526
+ logger.error(f"Failed to save feedback to {filename}: {e}")
527
+
528
  def create_interface(self) -> gr.Blocks:
529
  """Create and return the Gradio interface."""
530
 
 
595
  lines=10,
596
  interactive=False
597
  )
598
+
599
+ # --- FEEDBACK SECTION ---
600
+ gr.Markdown("### 📝 Provide Feedback on Translation")
601
+
602
+ with gr.Row():
603
+ notation = gr.Slider(
604
+ minimum=1,
605
+ maximum=5,
606
+ step=1,
607
+ label="Rate translation quality (1-5 stars)",
608
+ value=None,
609
+ interactive=True
610
+ )
611
+
612
+ correct_translation = gr.Textbox(
613
+ label="Correct Translation (if incorrect)",
614
+ placeholder="Please provide the correct version if inaccurate...",
615
+ lines=4,
616
+ value=""
617
+ )
618
+
619
+ submit_feedback = gr.Button("📤 Submit Feedback", variant="primary")
620
+ feedback_status = gr.Textbox(label="Feedback Status", value="", interactive=False)
621
+
622
+ # Hidden states to preserve context for feedback
623
+ user_input_state = gr.State()
624
+ model_output_state = gr.State()
625
+ source_lang_state = gr.State()
626
+ target_lang_state = gr.State()
627
+
628
  # Event handlers
629
  def update_visibility(mode: str) -> Dict[str, Any]:
630
  """Update component visibility based on input mode."""
 
657
  logger.error(f"Processing error: {e}")
658
  return "", f"❌ Error: {str(e)}"
659
 
660
+ def handle_translate_with_input(
661
  extracted_text: str,
662
  source_lang: str,
663
  target_lang: str
664
+ ) -> Tuple[str, str, str]:
665
+ """Handle translation and return inputs for logging."""
666
  if not extracted_text.strip():
667
+ return "📝 No text to translate.", "", ""
668
  try:
669
+ translated = self.translation_service.translate(
670
  extracted_text,
671
  Language(source_lang),
672
  Language(target_lang)
673
  )
674
+ return translated, extracted_text, source_lang
675
  except Exception as e:
676
  logger.error(f"Translation error: {e}")
677
+ error_msg = f"❌ Translation error: {str(e)}"
678
+ return error_msg, extracted_text, source_lang
679
+
680
+ def set_states_for_feedback(
681
+ output: str,
682
+ inp: str,
683
+ src: str,
684
+ tgt: str
685
+ ):
686
+ """Set hidden states for feedback submission."""
687
+ return inp, output, src, tgt
688
+
689
+ def save_feedback(
690
+ user_input: str,
691
+ model_output: str,
692
+ source_lang_str: str,
693
+ target_lang_str: str,
694
+ notation_val: float,
695
+ correct_trans: str
696
+ ):
697
+ """Save feedback to CSV."""
698
+ try:
699
+ source_lang = Language(source_lang_str)
700
+ target_lang = Language(target_lang_str)
701
+
702
+ self.log_feedback(
703
+ source_lang=source_lang,
704
+ target_lang=target_lang,
705
+ user_input=user_input,
706
+ model_output=model_output,
707
+ notation=notation_val,
708
+ correct_translation=correct_trans or None
709
+ )
710
+ return "✅ Thank you for your feedback!"
711
+ except Exception as e:
712
+ logger.error(f"Feedback submission failed: {e}")
713
+ return f"❌ Failed to save feedback: {str(e)}"
714
+
715
  # Connect events
716
  input_mode.change(
717
  fn=update_visibility,
 
719
  outputs=[input_text, audio_input, file_input, extracted_text, output_text]
720
  )
721
 
722
+ # Chain: Process → Translate → Store context → Wait for feedback
723
+ process_event = translate_btn.click(
724
  fn=handle_process,
725
  inputs=[input_mode, input_lang, input_text, audio_input, file_input],
726
  outputs=[extracted_text, output_text]
727
+ )
728
+
729
+ process_event.success(
730
+ fn=handle_translate_with_input,
731
  inputs=[extracted_text, input_lang, output_lang],
732
+ outputs=[output_text, gr.State(), gr.State()]
733
+ ).success(
734
+ fn=set_states_for_feedback,
735
+ inputs=[output_text, extracted_text, input_lang, output_lang],
736
+ outputs=[user_input_state, model_output_state, source_lang_state, target_lang_state]
737
+ )
738
+
739
+ # Feedback submission
740
+ submit_feedback.click(
741
+ fn=save_feedback,
742
+ inputs=[
743
+ user_input_state,
744
+ model_output_state,
745
+ source_lang_state,
746
+ target_lang_state,
747
+ notation,
748
+ correct_translation
749
+ ],
750
+ outputs=feedback_status
751
  )
752
 
753
  return interface