import os import gradio as gr import openai import whisper from gtts import gTTS from fpdf import FPDF import pandas as pd import time # 🔐 Set your OpenAI API key securely from environment variables openai.api_key = os.environ.get("OPENAI_API_KEY") # 🎙️ Load Whisper ASR model model = whisper.load_model("base") # 📁 CSV log file LOG_FILE = "toefl_logs.csv" if not os.path.exists(LOG_FILE): pd.DataFrame(columns=["timestamp", "prompt", "transcript", "feedback"]).to_csv(LOG_FILE, index=False) # 📝 TOEFL speaking prompts prompts = [ "Do you agree or disagree with the idea that people learn more from failure than from success?", "Some people believe that couples should live together before marriage. Do you agree or disagree?", "Describe a teacher who has greatly influenced your life. What qualities did they have?" ] # 🌐 Translations for multilingual UI labels translations = { "English": { "audio_label": "Audio Feedback", "pdf_label": "Download PDF", "share_label": "Share App", "feedback_history": "📜 Feedback History", "record_label": "🎤 Record your answer", "prompt_label": "📝 Choose a TOEFL Prompt", "language_label": "🌐 Language", "submit_label": "✅ Submit", "share_button_label": "🔗 Share App", "transcript_label": "🗣️ Your Transcript", "feedback_label": "✅ AI Feedback", }, "Pashto": { "audio_label": "د فېډبک آډیو", "pdf_label": "د فېډبک PDF", "share_label": "اپ شریک کړئ", "feedback_history": "د بیاکتنې تاریخ", "record_label": "🎤 خپل ځواب ثبت کړئ", "prompt_label": "📝 د TOEFL موضوع غوره کړئ", "language_label": "🌐 ژبه", "submit_label": "✅ وړاندې کول", "share_button_label": "🔗 اپ شریک کړئ", "transcript_label": "🗣️ ستاسو متن", "feedback_label": "✅ AI فیډبیک", }, "Dari": { "audio_label": "صدای بازخورد", "pdf_label": "دانلود PDF بازخورد", "share_label": "اشتراک اپلیکیشن", "feedback_history": "تاریخچه بازخورد", "record_label": "🎤 پاسخ خود را ضبط کنید", "prompt_label": "📝 یک موضوع TOEFL انتخاب کنید", "language_label": "🌐 زبان", "submit_label": "✅ ارسال", "share_button_label": "🔗 اشتراک اپ", "transcript_label": "🗣️ متن شما", "feedback_label": "✅ بازخورد AI", } } # 🎯 Transcribe audio file to text def transcribe(audio): result = model.transcribe(audio) return result["text"] # 🤖 Generate GPT feedback and score def get_feedback(prompt, response): full_prompt = f"""You are an English teacher scoring TOEFL Speaking responses. Prompt: "{prompt}" Student response: "{response}" Give detailed, kind feedback and a score out of 4. """ completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": full_prompt}] ) return completion.choices[0].message.content.strip() # 🔉 Generate TTS audio for feedback text def generate_audio_feedback(text): tts = gTTS(text) path = f"feedback_{int(time.time())}.mp3" tts.save(path) return path # 📄 Create a PDF with prompt, transcript, and feedback def generate_pdf(prompt, transcript, feedback): pdf = FPDF() pdf.add_page() pdf.set_font("Arial", size=12) pdf.multi_cell(0, 10, f"Prompt:\n{prompt}\n\nYour Response:\n{transcript}\n\nFeedback:\n{feedback}") path = f"feedback_{int(time.time())}.pdf" pdf.output(path) return path # 🧠 Main function triggered by Gradio def toefl_app(audio, selected_prompt, lang): transcript = transcribe(audio) feedback = get_feedback(selected_prompt, transcript) audio_path = generate_audio_feedback(feedback) pdf_path = generate_pdf(selected_prompt, transcript, feedback) # Log response to CSV timestamp = time.strftime("%Y-%m-%d %H:%M:%S") log_df = pd.read_csv(LOG_FILE) log_df.loc[len(log_df)] = [timestamp, selected_prompt, transcript, feedback] log_df.to_csv(LOG_FILE, index=False) labels = translations[lang] return ( transcript, feedback, labels["audio_label"], audio_path, labels["pdf_label"], pdf_path, START_BEEP, STOP_BEEP ) # 📜 Show last 20 feedback history entries def view_history(): df = pd.read_csv(LOG_FILE) return df.tail(20).to_markdown() # Update UI labels on language change def update_ui_labels(lang): labels = translations[lang] return ( labels["record_label"], labels["prompt_label"], labels["language_label"], labels["submit_label"], labels["share_button_label"], labels["transcript_label"], labels["feedback_label"], labels["audio_label"], labels["pdf_label"], labels["feedback_history"], ) # 🌍 Language options for UI lang_options = list(translations.keys()) # Beep sounds (upload these mp3 files to your repo) START_BEEP = "start_beep.mp3" STOP_BEEP = "stop_beep.mp3" # 📱 Gradio UI with gr.Blocks(css=""" footer {display: none !important;} .gradio-container { max-width: 600px; margin-left: auto; margin-right: auto; } button, .gr-button { font-size: 1.1em; padding: 0.7em 1.2em; } """) as demo: gr.Markdown("## 🗣️ TOEFL Speaking Practice Tool | [by @Nazari11221](https://huggingface.co/Nazari11221)") with gr.Row(): audio = gr.Audio(type="filepath", label=translations["English"]["record_label"]) prompt = gr.Dropdown(prompts, label=translations["English"]["prompt_label"]) lang = gr.Dropdown(lang_options, value="English", label=translations["English"]["language_label"]) with gr.Row(): submit = gr.Button(translations["English"]["submit_label"]) share = gr.Button(translations["English"]["share_button_label"]) transcript = gr.Textbox(label=translations["English"]["transcript_label"]) feedback = gr.Textbox(label=translations["English"]["feedback_label"]) audio_label = gr.Label(value=translations["English"]["audio_label"]) audio_out = gr.Audio() pdf_label = gr.Label(value=translations["English"]["pdf_label"]) pdf_out = gr.File() beep_start = gr.Audio(START_BEEP, visible=False, autoplay=True) beep_end = gr.Audio(STOP_BEEP, visible=False, autoplay=True) with gr.Accordion(translations["English"]["feedback_history"], open=False) as feedback_history: hist_output = gr.Markdown() hist_btn = gr.Button("🔄 Refresh History") # Connect buttons and events submit.click( toefl_app, inputs=[audio, prompt, lang], outputs=[transcript, feedback, audio_label, audio_out, pdf_label, pdf_out, beep_start, beep_end], ) hist_btn.click(view_history, outputs=hist_output) share.click(lambda: "🔗 Share this link: https://nazari11221-toefl-speaking-practice.hf.space", outputs=share) # Update all UI labels when language changes lang.change( update_ui_labels, inputs=[lang], outputs=[audio, prompt, lang, submit, share, transcript, feedback, audio_label, pdf_label, feedback_history] ) if __name__ == "__main__": demo.launch()