import gradio as gr
import uuid
import os
import matplotlib.pyplot as plt
from openai import OpenAI
from app_utils import (
LANG_CODES, save_to_db, fetch_user_sessions,
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_written_feedback(text, language, goal="general improvement", focus_areas=None, previous_text=None):
focus_str = ", ".join(focus_areas) if focus_areas else "Clarity, Structure, Grammar, Style, Tone, Creativity"
history_section = f"\n\nCompare this to their previous writing:\n{previous_text}" if previous_text else ""
prompt = f"""
You are a writing coach helping a student improve their writing for the following goal: **{goal}**.
First, return a JSON object with scores (0–10) for each of these:
{focus_str}
For each area:
- Repeat the score (0–10)
- Detailed explaination of why the user got that score
Then add
- A detailed summary of strengths and improvements
- One motivational line, without using the actual motivation keyword
Current text:
{text}
{history_section}
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": f"You are a helpful writing tutor responding in {language}."},
{"role": "user", "content": prompt}
],
temperature=0.7
)
feedback = response.choices[0].message.content
# Strip JSON before returning
try:
split_idx = feedback.index('}') + 1
feedback_clean = feedback[split_idx:].strip()
except:
feedback_clean = feedback
return feedback, feedback_clean
def improve_written_text(text, language):
prompt = f"""Improve this writing by refining structure, grammar, and clarity — without changing its meaning.
Text:
{text}
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": f"Reply in {language}. Provide only the improved version."},
{"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 data yet", ha='center', va='center', fontsize=12)
ax.axis('off')
return fig
def written_dashboard(nickname_input):
with gr.Column() as written_panel:
gr.Markdown("""
""")
with gr.Row():
language_dropdown = gr.Dropdown(label="🌍 Language", choices=list(LANG_CODES.keys()), value="English")
goal_dropdown = gr.Dropdown(label="🎯 Writing Goal", choices=["Essay for school", "Job application", "Blog post", "Creative writing", "General improvement"], value="General improvement")
focus_checkboxes = gr.CheckboxGroup(label="🧠 Focus Areas", choices=["Clarity", "Organization", "Grammar", "Vocabulary", "Tone", "Creativity"], value=["Clarity", "Organization", "Grammar", "Vocabulary", "Tone", "Creativity"])
input_text = gr.Textbox(label="📝 Your Writing", lines=10, elem_id="input_text", elem_classes=["text_area"], interactive=True)
submit_button = gr.Button("🚀 Submit", variant="primary", size="sm")
file_input = gr.File(label="📄 Upload Text File", file_types=[".txt", ".md"])
feedback_box = gr.Textbox(label="💡 Feedback", interactive=False)
improved_box = gr.Textbox(label="🎯 Improved Version", interactive=False)
history_table = gr.Dataframe(headers=["🕒 Timestamp", "🌐 Language", "✍️ Text", "📋 Feedback"])
# === Chart Area ===
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 extract_text(file):
if file is None:
return ""
with open(file.name, 'r', encoding='utf-8') as f:
return f.read()
file_input.change(fn=extract_text, inputs=file_input, outputs=input_text)
def tutor_written(text, language, goal, focus_areas, trend_category, nickname):
if hasattr(nickname, "value"):
nickname = nickname.value
if not text.strip():
return "", "", [], render_empty_chart("📊 Score Comparison"), render_empty_chart("📈 Progress Over Time"), gr.update(visible=False)
sessions = fetch_user_sessions(nickname)
previous_text = sessions[-1].transcript if sessions else None
previous_feedback = sessions[-1].feedback if sessions else None
full_feedback, feedback_clean = generate_written_feedback(text, language, goal, focus_areas, previous_text)
improved = improve_written_text(text, language)
save_to_db(nickname, text, 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")
milestone_msg = ""
if len(sessions) in [3, 5, 10]:
milestone_msg = f"🎉 You’ve completed **{len(sessions)} writing sessions**!"
feedback_clean += f"\n\n{milestone_msg}"
return feedback_clean, improved, session_table, score_plot, trend_plot, gr.update(visible=bool(milestone_msg), value=milestone_msg)
submit_button.click(
fn=tutor_written,
inputs=[input_text, language_dropdown, goal_dropdown, focus_checkboxes, trend_category_dropdown, nickname_input],
outputs=[feedback_box, improved_box, history_table, score_chart, trend_chart, milestone_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 written_panel