import gradio as gr
import uuid
import os
import matplotlib.pyplot as plt
from gtts import gTTS
from openai import OpenAI
from app_utils import (
LANG_CODES, save_to_db, fetch_user_sessions,
convert_to_wav, transcribe_audio,
parse_scores_from_feedback, generate_progress_summary,
build_score_comparison_data, render_score_chart,
build_trend_data, render_trend_chart
)
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def generate_feedback(transcript, language, goal="general improvement", focus_areas=None, previous_transcript=None):
focus_str = ", ".join(focus_areas) if focus_areas else "Clarity, Structure, Fluency, Content Relevance, and Tone"
history_section = f"\n\nFor reference, their previous transcript was:\n{previous_transcript}" if previous_transcript else ""
prompt = f"""
You are a supportive communication coach helping a learner whose goal is: **{goal}**.
First, return a JSON object of scores (0–10) for these areas:
{focus_str}
Then provide a clear and friendly evaluation:
For each area:
- Repeat the score (0–10)
- Explain why the user got that score
Then provide:
- A detailed summary of strengths and improvement areas.
- One motivational line to end with. Do not specifically add motivational keyword, just add the line.
Transcript:
{history_section}
""".strip()
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": f"You are a warm and constructive communication coach responding in {language}."},
{"role": "user", "content": prompt}
],
temperature=0.7
)
feedback = response.choices[0].message.content
try:
split_idx = feedback.index('}') + 1
feedback_clean = feedback[split_idx:].strip()
except:
feedback_clean = feedback
return feedback, feedback_clean
def generate_example_response(transcript, language):
prompt = f"""Rewrite this speech to make it more polished, fluent, and confident.
Keep the meaning and tone the same, but improve clarity and structure.
Transcript:
{transcript}
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": f"Reply in {language}. Provide only the improved version of the speech."},
{"role": "user", "content": prompt}
]
)
return response.choices[0].message.content
def render_empty_chart(title):
fig, ax = plt.subplots()
ax.set_title(title)
ax.text(0.5, 0.5, "No scores yet.\nSpeak again to generate progress!", ha='center', va='center', fontsize=12)
ax.axis('off')
return fig
def spoken_dashboard(nickname_input):
with gr.Column() as spoken_panel:
gr.Markdown("""
""")
with gr.Row():
language_dropdown = gr.Dropdown(label="🌍 Language", choices=list(LANG_CODES.keys()), value="English")
goal_dropdown = gr.Dropdown(label="🎯 Goal", choices=["Interview preparation", "Public speaking", "Class presentation", "General improvement"], value="General improvement")
focus_checkboxes = gr.CheckboxGroup(label="🧠 Focus Areas", choices=["Clarity", "Structure", "Fluency", "Tone", "Content Relevance"], value=["Clarity", "Structure", "Fluency", "Tone", "Content Relevance"])
with gr.Row():
audio_input = gr.Audio(type="filepath", label="🎙 Speak or Upload Audio")
audio_output = gr.Audio(label="🔊 Chatter's Response", type="filepath")
transcript_box = gr.Textbox(label="📖 Transcript", interactive=False)
feedback_box = gr.Textbox(label="💡 Feedback", interactive=False)
hidden_transcript = gr.Textbox(visible=False)
with gr.Row():
try_again = gr.Button("🔁 Try Again")
show_example = gr.Button("🎯 Show Me an Example")
example_box = gr.Textbox(label="🗣 Suggested Improvement", visible=True)
history_table = gr.Dataframe(headers=["🕒 Timestamp", "🌐 Language", "📄 Transcript", "💬 Feedback"])
with gr.Row():
with gr.Column(scale=1):
gr.Dropdown(
choices=[""],
label="📊 Score Comparison",
interactive=False,
show_label=True
)
score_chart = gr.Plot()
with gr.Column(scale=1):
trend_category_dropdown = gr.Dropdown(
label="📈 Track Progress In",
choices=["Clarity", "Structure", "Fluency", "Tone", "Content Relevance"],
value="Tone"
)
trend_chart = gr.Plot(label="Progress Over Time")
milestone_box = gr.Markdown(visible=False)
def tutor_feedback(audio_file, language, goal, focus_areas, trend_category, nickname):
if not audio_file:
return "", "No audio received.", None, "", [], render_empty_chart("📊 Score Comparison"), render_empty_chart("📈 Progress Over Time"), gr.update(visible=False)
if hasattr(nickname, "value"):
nickname = nickname.value
wav_path = convert_to_wav(audio_file)
transcript = transcribe_audio(wav_path)
previous_sessions = fetch_user_sessions(nickname)
previous_transcript = previous_sessions[-1].transcript if previous_sessions else None
previous_feedback = previous_sessions[-1].feedback if previous_sessions else None
full_feedback, feedback_clean = generate_feedback(transcript, language, goal, focus_areas, previous_transcript)
if previous_feedback:
feedback_clean += generate_progress_summary(full_feedback, previous_feedback)
milestone = ""
session_count = len(previous_sessions) + 1
if session_count in [3, 5, 10]:
milestone = f"🎉 Congrats on completing **{session_count} sessions**!"
feedback_clean += f"\n\n{milestone}"
lang_code = LANG_CODES.get(language, "en")
tts = gTTS(feedback_clean, lang=lang_code)
mp3_path = f"/tmp/{uuid.uuid4()}.mp3"
tts.save(mp3_path)
save_to_db(nickname, transcript, full_feedback, language)
sessions = fetch_user_sessions(nickname)
session_table = [[s.timestamp, s.language, s.transcript[:40], s.feedback[:40]] for s in sessions]
score_plot = render_score_chart(build_score_comparison_data(full_feedback, previous_feedback)) if previous_feedback else render_empty_chart("📊 Score Comparison")
dates, trend_scores = build_trend_data(sessions, trend_category)
trend_plot = render_trend_chart(dates, trend_scores, trend_category) if trend_scores else render_empty_chart(f"📈 {trend_category} Progress")
return transcript, feedback_clean, mp3_path, transcript, session_table, score_plot, trend_plot, gr.update(visible=bool(milestone), value=milestone)
audio_input.change(
fn=tutor_feedback,
inputs=[audio_input, language_dropdown, goal_dropdown, focus_checkboxes, trend_category_dropdown, nickname_input],
outputs=[transcript_box, feedback_box, audio_output, hidden_transcript, history_table, score_chart, trend_chart, milestone_box],
show_progress="minimal"
)
try_again.click(fn=lambda: ("", "", None, "", "", render_empty_chart("📊 Score Comparison"), render_empty_chart("📈 Progress Over Time"), gr.update(visible=False)),
inputs=None,
outputs=[transcript_box, feedback_box, audio_output, hidden_transcript, example_box, score_chart, trend_chart, milestone_box])
show_example.click(fn=generate_example_response,
inputs=[hidden_transcript, language_dropdown],
outputs=example_box)
def update_trend_chart(trend_category, nickname):
if hasattr(nickname, "value"):
nickname = nickname.value
sessions = fetch_user_sessions(nickname)
dates, trend_scores = build_trend_data(sessions, trend_category)
if trend_scores:
return render_trend_chart(dates, trend_scores, trend_category)
return render_empty_chart(f"📈 {trend_category} Progress")
trend_category_dropdown.change(
fn=update_trend_chart,
inputs=[trend_category_dropdown, nickname_input],
outputs=[trend_chart]
)
return spoken_panel