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