| 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 |
|
|
| |
| 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(""" |
| <div id="header" style="text-align: center;"> |
| <h2>βοΈ Written Communication with <strong>Chatter the Owl</strong></h2> |
| <p>Paste or upload your writing. Select your goal and focus, and receive personalized feedback with progress tracking.</p> |
| </div> |
| """) |
|
|
| 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"]) |
|
|
| |
| 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 |
|
|