Spaces:
Runtime error
Runtime error
| 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() |