import gradio as gr import pandas as pd import numpy as np import json import os from pathlib import Path from datetime import datetime, date import matplotlib.pyplot as plt # ========================================================= # StudyPilot AI # Reinforcement Learning based Smart Study Planner # Hugging Face Gradio Deployment Ready # ========================================================= DATA_DIR = Path("data") DATA_DIR.mkdir(exist_ok=True) PROFILE_FILE = DATA_DIR / "profile.json" SUBJECTS_FILE = DATA_DIR / "subjects.csv" TOPICS_FILE = DATA_DIR / "topics.csv" Q_TABLE_FILE = DATA_DIR / "q_table.json" LOGS_FILE = DATA_DIR / "study_logs.csv" ACTIONS = [ "study_new_topic", "revise_topic", "practice_questions", "take_quiz", "quick_review" ] DEFAULT_PROFILE = { "name": "Student", "exam_type": "HSC", "exam_date": str(date.today()), "daily_hours": 3, "target_grade": "A+", "study_mode": "Balanced" } # ========================================================= # Storage Helpers # ========================================================= def ensure_files(): if not PROFILE_FILE.exists(): save_json(PROFILE_FILE, DEFAULT_PROFILE) if not SUBJECTS_FILE.exists(): pd.DataFrame(columns=[ "subject", "importance", "confidence" ]).to_csv(SUBJECTS_FILE, index=False) if not TOPICS_FILE.exists(): pd.DataFrame(columns=[ "subject", "topic", "difficulty", "importance", "estimated_hours", "confidence", "quiz_score", "completed", "last_studied", "skip_count" ]).to_csv(TOPICS_FILE, index=False) if not Q_TABLE_FILE.exists(): save_json(Q_TABLE_FILE, {}) if not LOGS_FILE.exists(): pd.DataFrame(columns=[ "date", "subject", "topic", "action", "completed", "study_minutes", "old_score", "new_score", "reward" ]).to_csv(LOGS_FILE, index=False) def load_json(path, default=None): if not path.exists(): return default if default is not None else {} try: with open(path, "r", encoding="utf-8") as f: return json.load(f) except Exception: return default if default is not None else {} def save_json(path, data): with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) def load_subjects(): ensure_files() return pd.read_csv(SUBJECTS_FILE) def save_subjects(df): df.to_csv(SUBJECTS_FILE, index=False) def load_topics(): ensure_files() df = pd.read_csv(TOPICS_FILE) if not df.empty: df["completed"] = df["completed"].astype(str).map( {"True": True, "False": False, "true": True, "false": False, "1": True, "0": False} ).fillna(False) numeric_cols = ["difficulty", "importance", "estimated_hours", "confidence", "quiz_score", "skip_count"] for col in numeric_cols: if col in df.columns: df[col] = pd.to_numeric(df[col], errors="coerce").fillna(0) return df def save_topics(df): df.to_csv(TOPICS_FILE, index=False) def load_q_table(): ensure_files() return load_json(Q_TABLE_FILE, {}) def save_q_table(q_table): save_json(Q_TABLE_FILE, q_table) def load_logs(): ensure_files() return pd.read_csv(LOGS_FILE) def save_logs(df): df.to_csv(LOGS_FILE, index=False) ensure_files() # ========================================================= # Core Logic # ========================================================= def safe_float(x, default=0.0): try: if x is None or x == "": return default return float(x) except Exception: return default def parse_date(date_str): try: return datetime.strptime(str(date_str), "%Y-%m-%d").date() except Exception: return date.today() def get_days_left(): profile = load_json(PROFILE_FILE, DEFAULT_PROFILE) exam_date = parse_date(profile.get("exam_date", str(date.today()))) return max((exam_date - date.today()).days, 0) def topic_key(subject, topic): return f"{subject}::{topic}" def get_q_values(subject, topic): q_table = load_q_table() key = topic_key(subject, topic) if key not in q_table: q_table[key] = {action: 0.5 for action in ACTIONS} save_q_table(q_table) return q_table[key] def best_action(subject, topic, study_mode="Balanced"): q_values = get_q_values(subject, topic) if study_mode == "Revision Focused": q_values["revise_topic"] += 0.10 q_values["quick_review"] += 0.08 elif study_mode == "Weakness Killer": q_values["practice_questions"] += 0.12 q_values["take_quiz"] += 0.08 elif study_mode == "Exam Crash": q_values["revise_topic"] += 0.14 q_values["practice_questions"] += 0.12 q_values["quick_review"] += 0.10 elif study_mode == "Confidence Builder": q_values["quick_review"] += 0.08 q_values["study_new_topic"] += 0.04 return max(q_values, key=q_values.get), max(q_values.values()) def days_since_last_studied(last_studied): try: d = datetime.strptime(str(last_studied), "%Y-%m-%d").date() return max((date.today() - d).days, 0) except Exception: return 30 def calculate_topic_priority(row, study_mode="Balanced"): days_left = max(get_days_left(), 1) quiz_score = safe_float(row.get("quiz_score", 0)) confidence = safe_float(row.get("confidence", 5)) difficulty = safe_float(row.get("difficulty", 5)) importance = safe_float(row.get("importance", 5)) completed = bool(row.get("completed", False)) skip_count = safe_float(row.get("skip_count", 0)) weakness_score = max(0, 100 - quiz_score) / 100 confidence_weakness = max(0, 10 - confidence) / 10 revision_need = min(days_since_last_studied(row.get("last_studied", "")) / 14, 1.0) exam_pressure = min(30 / days_left, 3.0) / 3.0 incomplete_bonus = 0 if completed else 1 action, q_value = best_action(row["subject"], row["topic"], study_mode) # Mode weights if study_mode == "Weakness Killer": w_weakness, w_importance, w_revision = 0.38, 0.24, 0.10 elif study_mode == "Revision Focused": w_weakness, w_importance, w_revision = 0.22, 0.24, 0.30 elif study_mode == "Exam Crash": w_weakness, w_importance, w_revision = 0.25, 0.33, 0.22 elif study_mode == "Confidence Builder": w_weakness, w_importance, w_revision = 0.24, 0.22, 0.16 else: w_weakness, w_importance, w_revision = 0.30, 0.27, 0.16 score = ( (importance / 10) * w_importance + (difficulty / 10) * 0.10 + weakness_score * w_weakness + confidence_weakness * 0.10 + revision_need * w_revision + exam_pressure * 0.10 + incomplete_bonus * 0.05 + min(skip_count / 5, 1) * 0.04 + q_value * 0.08 ) return round(score * 100, 2), action def explain_reason(row, action): reasons = [] quiz = safe_float(row.get("quiz_score", 0)) confidence = safe_float(row.get("confidence", 5)) importance = safe_float(row.get("importance", 5)) difficulty = safe_float(row.get("difficulty", 5)) last_days = days_since_last_studied(row.get("last_studied", "")) if quiz < 50: reasons.append("quiz score is low") if confidence <= 4: reasons.append("confidence is low") if importance >= 8: reasons.append("exam importance is high") if difficulty >= 8: reasons.append("topic difficulty is high") if last_days >= 7: reasons.append("revision gap is large") if not reasons: reasons.append("it is useful for steady progress") action_text = action.replace("_", " ") return f"Recommended action: {action_text}. Reason: " + ", ".join(reasons) + "." def action_to_task(action): mapping = { "study_new_topic": "Study concept + make short notes", "revise_topic": "Revise notes + solve examples", "practice_questions": "Practice MCQ/CQ/problem solving", "take_quiz": "Take quiz and review mistakes", "quick_review": "Quick revision and formula review" } return mapping.get(action, "Study this topic") def generate_today_plan(max_topics=3): profile = load_json(PROFILE_FILE, DEFAULT_PROFILE) study_mode = profile.get("study_mode", "Balanced") daily_hours = max(safe_float(profile.get("daily_hours", 3), 3), 1) topics = load_topics() if topics.empty: return "No topics added yet. Please add subjects and topics first.", pd.DataFrame(), None scored_rows = [] for _, row in topics.iterrows(): score, action = calculate_topic_priority(row, study_mode) scored_rows.append({ "Priority Score": score, "Subject": row["subject"], "Topic": row["topic"], "Recommended Task": action_to_task(action), "Action": action, "Quiz Score": row["quiz_score"], "Confidence": row["confidence"], "Importance": row["importance"], "Difficulty": row["difficulty"], "Reason": explain_reason(row, action) }) plan_df = pd.DataFrame(scored_rows).sort_values("Priority Score", ascending=False).head(int(max_topics)) total_priority = plan_df["Priority Score"].sum() total_minutes = int(daily_hours * 60) if total_priority <= 0: plan_df["Study Time"] = int(total_minutes / len(plan_df)) else: plan_df["Study Time"] = plan_df["Priority Score"].apply( lambda x: max(20, int((x / total_priority) * total_minutes)) ) days_left = get_days_left() readiness = calculate_readiness() message = f""" # Today’s Smart Study Plan **Exam in:** {days_left} days **Study Mode:** {study_mode} **Daily Study Time:** {daily_hours} hours **Overall Readiness:** {readiness}% Start with the highest priority topic first. The app ranks topics using quiz score, confidence, importance, revision gap, exam pressure, and RL memory. """ fig = make_priority_chart(plan_df) return message, plan_df, fig def calculate_readiness(): topics = load_topics() if topics.empty: return 0 quiz_component = topics["quiz_score"].clip(0, 100).mean() confidence_component = (topics["confidence"].clip(0, 10).mean() / 10) * 100 completion_component = topics["completed"].astype(bool).mean() * 100 if len(topics) else 0 readiness = quiz_component * 0.45 + confidence_component * 0.30 + completion_component * 0.25 return round(float(readiness), 1) def calculate_risk_label(readiness): if readiness >= 80: return "Low Risk" elif readiness >= 60: return "Medium Risk" elif readiness >= 40: return "High Risk" return "Very High Risk" def make_priority_chart(plan_df): if plan_df is None or plan_df.empty: return None fig, ax = plt.subplots(figsize=(8, 4)) labels = [f"{s}\n{t}" for s, t in zip(plan_df["Subject"], plan_df["Topic"])] ax.bar(labels, plan_df["Priority Score"]) ax.set_title("Today’s Topic Priority") ax.set_ylabel("Priority Score") ax.tick_params(axis="x", labelrotation=20) plt.tight_layout() return fig def make_subject_progress_chart(): topics = load_topics() if topics.empty: return None grouped = topics.groupby("subject").agg( avg_quiz=("quiz_score", "mean"), avg_confidence=("confidence", "mean"), completion=("completed", lambda x: x.astype(bool).mean() * 100) ).reset_index() grouped["readiness"] = ( grouped["avg_quiz"] * 0.50 + (grouped["avg_confidence"] / 10 * 100) * 0.25 + grouped["completion"] * 0.25 ) fig, ax = plt.subplots(figsize=(8, 4)) ax.bar(grouped["subject"], grouped["readiness"]) ax.set_ylim(0, 100) ax.set_title("Subject Readiness") ax.set_ylabel("Readiness %") ax.tick_params(axis="x", labelrotation=20) plt.tight_layout() return fig def calculate_reward(old_score, new_score, completed, study_minutes, topic_row): reward = 0 old_score = safe_float(old_score, 0) new_score = safe_float(new_score, 0) study_minutes = safe_float(study_minutes, 0) importance = safe_float(topic_row.get("importance", 5), 5) quiz_improvement = new_score - old_score if completed: reward += 20 else: reward -= 10 if quiz_improvement > 0: reward += min(30, quiz_improvement) elif quiz_improvement < 0: reward -= min(15, abs(quiz_improvement)) if old_score < 50 and completed: reward += 15 if importance >= 8 and completed: reward += 10 if study_minutes >= 30: reward += 10 if not completed and importance >= 8: reward -= 15 return round(float(reward), 2) def update_q_table(subject, topic, action, reward): q_table = load_q_table() key = topic_key(subject, topic) if key not in q_table: q_table[key] = {a: 0.5 for a in ACTIONS} if action not in q_table[key]: q_table[key][action] = 0.5 old_q = q_table[key][action] learning_rate = 0.12 discount_factor = 0.90 max_future_q = max(q_table[key].values()) normalized_reward = reward / 100 new_q = old_q + learning_rate * (normalized_reward + discount_factor * max_future_q - old_q) q_table[key][action] = round(float(new_q), 4) save_q_table(q_table) return old_q, new_q # ========================================================= # Gradio Functions # ========================================================= def save_profile(name, exam_type, exam_date, daily_hours, target_grade, study_mode): profile = { "name": name or "Student", "exam_type": exam_type or "General Exam", "exam_date": exam_date, "daily_hours": safe_float(daily_hours, 3), "target_grade": target_grade or "A+", "study_mode": study_mode or "Balanced" } save_json(PROFILE_FILE, profile) return f"Profile saved for {profile['name']}." def get_profile_summary(): profile = load_json(PROFILE_FILE, DEFAULT_PROFILE) days_left = get_days_left() readiness = calculate_readiness() risk = calculate_risk_label(readiness) return f""" # StudyPilot AI Dashboard **Student:** {profile.get("name", "Student")} **Exam:** {profile.get("exam_type", "Exam")} **Exam Date:** {profile.get("exam_date", "")} **Days Left:** {days_left} **Daily Study Hours:** {profile.get("daily_hours", 3)} **Study Mode:** {profile.get("study_mode", "Balanced")} **Overall Readiness:** {readiness}% **Risk Level:** {risk} """ def add_subject(subject, importance, confidence): subject = str(subject).strip() if not subject: return "Subject name is required.", load_subjects() df = load_subjects() if not df.empty and subject.lower() in df["subject"].astype(str).str.lower().values: return "This subject already exists.", df new_row = pd.DataFrame([{ "subject": subject, "importance": int(safe_float(importance, 5)), "confidence": int(safe_float(confidence, 5)) }]) df = pd.concat([df, new_row], ignore_index=True) save_subjects(df) return f"Subject added: {subject}", df def add_topic(subject, topic, difficulty, importance, estimated_hours, confidence, quiz_score): subject = str(subject).strip() topic = str(topic).strip() if not subject or not topic: return "Subject and topic are required.", load_topics() subjects = load_subjects() if subjects.empty or subject.lower() not in subjects["subject"].astype(str).str.lower().values: # Auto-create subject for convenience subjects = pd.concat([subjects, pd.DataFrame([{ "subject": subject, "importance": 7, "confidence": 5 }])], ignore_index=True) save_subjects(subjects) df = load_topics() duplicate = False if not df.empty: duplicate = ((df["subject"].astype(str).str.lower() == subject.lower()) & (df["topic"].astype(str).str.lower() == topic.lower())).any() if duplicate: return "This topic already exists under this subject.", df new_row = pd.DataFrame([{ "subject": subject, "topic": topic, "difficulty": int(safe_float(difficulty, 5)), "importance": int(safe_float(importance, 5)), "estimated_hours": safe_float(estimated_hours, 2), "confidence": int(safe_float(confidence, 5)), "quiz_score": int(safe_float(quiz_score, 0)), "completed": False, "last_studied": str(date.today()), "skip_count": 0 }]) df = pd.concat([df, new_row], ignore_index=True) save_topics(df) # Create initial q values get_q_values(subject, topic) return f"Topic added: {subject} → {topic}", df def get_subject_choices(): df = load_subjects() if df.empty: return [] return sorted(df["subject"].dropna().astype(str).unique().tolist()) def get_topic_choices(): df = load_topics() if df.empty: return [] return [f"{row.subject} :: {row.topic}" for row in df.itertuples()] def refresh_all_tables(): return load_subjects(), load_topics(), load_logs(), get_profile_summary(), make_subject_progress_chart() def submit_feedback(topic_selection, action, completed, study_minutes, new_quiz_score, difficulty_after): if not topic_selection: return "Please select a topic.", load_topics(), load_logs(), make_subject_progress_chart() try: subject, topic = [x.strip() for x in topic_selection.split("::", 1)] except Exception: return "Invalid topic selection.", load_topics(), load_logs(), make_subject_progress_chart() topics = load_topics() mask = (topics["subject"].astype(str) == subject) & (topics["topic"].astype(str) == topic) if not mask.any(): return "Topic not found.", topics, load_logs(), make_subject_progress_chart() idx = topics[mask].index[0] old_score = safe_float(topics.loc[idx, "quiz_score"], 0) completed_bool = bool(completed) new_score = safe_float(new_quiz_score, old_score) topic_row = topics.loc[idx].to_dict() reward = calculate_reward(old_score, new_score, completed_bool, study_minutes, topic_row) old_q, new_q = update_q_table(subject, topic, action, reward) topics.loc[idx, "quiz_score"] = new_score topics.loc[idx, "completed"] = completed_bool topics.loc[idx, "confidence"] = difficulty_to_confidence(difficulty_after, topics.loc[idx, "confidence"]) topics.loc[idx, "last_studied"] = str(date.today()) if completed_bool: topics.loc[idx, "skip_count"] = max(0, safe_float(topics.loc[idx, "skip_count"], 0) - 1) else: topics.loc[idx, "skip_count"] = safe_float(topics.loc[idx, "skip_count"], 0) + 1 save_topics(topics) logs = load_logs() new_log = pd.DataFrame([{ "date": str(date.today()), "subject": subject, "topic": topic, "action": action, "completed": completed_bool, "study_minutes": safe_float(study_minutes, 0), "old_score": old_score, "new_score": new_score, "reward": reward }]) logs = pd.concat([logs, new_log], ignore_index=True) save_logs(logs) msg = f""" # Feedback Saved **Topic:** {subject} → {topic} **Reward:** {reward} points **Q-value updated:** {round(old_q, 4)} → {round(new_q, 4)} The planner will use this feedback to improve your next recommendation. """ return msg, topics, logs, make_subject_progress_chart() def difficulty_to_confidence(difficulty_after, current_confidence): current = safe_float(current_confidence, 5) if difficulty_after == "Easy": return min(10, current + 1) if difficulty_after == "Medium": return current if difficulty_after == "Hard": return max(1, current - 1) return current def weak_topics_table(): topics = load_topics() if topics.empty: return pd.DataFrame() df = topics.copy() df["weakness"] = 100 - df["quiz_score"] df = df.sort_values(["weakness", "importance"], ascending=False) return df[["subject", "topic", "quiz_score", "confidence", "importance", "difficulty", "completed"]].head(10) def ai_coach(question): profile = load_json(PROFILE_FILE, DEFAULT_PROFILE) topics = load_topics() readiness = calculate_readiness() risk = calculate_risk_label(readiness) days_left = get_days_left() if topics.empty: return "Add some subjects and topics first. Then I can coach you based on your progress." weakest = topics.sort_values(["quiz_score", "confidence"], ascending=[True, True]).head(1).iloc[0] strongest = topics.sort_values(["quiz_score", "confidence"], ascending=[False, False]).head(1).iloc[0] q = (question or "").lower() if "why" in q or "keno" in q or "কেন" in q: return f""" You should focus on **{weakest['subject']} → {weakest['topic']}** because your quiz score is **{weakest['quiz_score']}%**, confidence is **{weakest['confidence']}/10**, and importance is **{weakest['importance']}/10**. Exam is in **{days_left} days**, so weak and important topics should get priority. """ if "motivation" in q or "motivate" in q: return f""" You do not need to finish everything today. Your only goal is to improve one weak topic. Start with **{weakest['topic']}** for 25 minutes. Small progress daily beats panic study before exam. """ if "risk" in q or "readiness" in q: return f""" Your current readiness is **{readiness}%** and your risk level is **{risk}**. Weakest topic: **{weakest['subject']} → {weakest['topic']}** Strongest topic: **{strongest['subject']} → {strongest['topic']}** Improve weak topics first to reduce exam risk. """ return f""" Based on your current data: - Readiness: **{readiness}%** - Risk: **{risk}** - Days left: **{days_left}** - Weakest topic: **{weakest['subject']} → {weakest['topic']}** - Best topic: **{strongest['subject']} → {strongest['topic']}** Recommended next step: study your weakest high-importance topic first, then take a short quiz. """ def reset_demo_data(): save_json(PROFILE_FILE, DEFAULT_PROFILE) subjects = pd.DataFrame([ {"subject": "Math", "importance": 10, "confidence": 3}, {"subject": "Physics", "importance": 9, "confidence": 4}, {"subject": "English", "importance": 7, "confidence": 6} ]) save_subjects(subjects) topics = pd.DataFrame([ { "subject": "Math", "topic": "Differentiation", "difficulty": 9, "importance": 10, "estimated_hours": 5, "confidence": 3, "quiz_score": 35, "completed": False, "last_studied": str(date.today()), "skip_count": 1 }, { "subject": "Physics", "topic": "Current Electricity", "difficulty": 8, "importance": 9, "estimated_hours": 4, "confidence": 4, "quiz_score": 42, "completed": False, "last_studied": str(date.today()), "skip_count": 0 }, { "subject": "English", "topic": "Writing Part", "difficulty": 6, "importance": 8, "estimated_hours": 3, "confidence": 6, "quiz_score": 65, "completed": False, "last_studied": str(date.today()), "skip_count": 0 } ]) save_topics(topics) save_json(Q_TABLE_FILE, {}) save_logs(pd.DataFrame(columns=[ "date", "subject", "topic", "action", "completed", "study_minutes", "old_score", "new_score", "reward" ])) return "Demo data loaded.", subjects, topics, load_logs(), get_profile_summary(), make_subject_progress_chart() def clear_all_data(): save_json(PROFILE_FILE, DEFAULT_PROFILE) save_subjects(pd.DataFrame(columns=["subject", "importance", "confidence"])) save_topics(pd.DataFrame(columns=[ "subject", "topic", "difficulty", "importance", "estimated_hours", "confidence", "quiz_score", "completed", "last_studied", "skip_count" ])) save_json(Q_TABLE_FILE, {}) save_logs(pd.DataFrame(columns=[ "date", "subject", "topic", "action", "completed", "study_minutes", "old_score", "new_score", "reward" ])) return "All data cleared.", load_subjects(), load_topics(), load_logs(), get_profile_summary(), None # ========================================================= # Gradio UI # ========================================================= custom_css = """ #main-title { text-align: center; margin-bottom: 10px; } .metric-card { border-radius: 14px; padding: 16px; } """ with gr.Blocks( theme=gr.themes.Soft(), css=custom_css, title="StudyPilot AI" ) as demo: gr.Markdown( """ # StudyPilot AI ### Reinforcement Learning Based Smart Study Planner Dynamic subjects, adaptive topic recommendation, quiz feedback, reward system, and progress analytics. """, elem_id="main-title" ) with gr.Tab("Dashboard"): dashboard_md = gr.Markdown(get_profile_summary()) refresh_btn = gr.Button("Refresh Dashboard") subject_chart = gr.Plot(value=make_subject_progress_chart()) weak_table = gr.Dataframe(value=weak_topics_table(), label="Top Weak Topics", interactive=False) refresh_btn.click( fn=lambda: (get_profile_summary(), make_subject_progress_chart(), weak_topics_table()), outputs=[dashboard_md, subject_chart, weak_table] ) with gr.Tab("Student Profile"): with gr.Row(): name = gr.Textbox(label="Student Name", value=load_json(PROFILE_FILE, DEFAULT_PROFILE).get("name", "Student")) exam_type = gr.Textbox(label="Exam Type", value=load_json(PROFILE_FILE, DEFAULT_PROFILE).get("exam_type", "HSC")) with gr.Row(): exam_date = gr.Textbox(label="Exam Date YYYY-MM-DD", value=load_json(PROFILE_FILE, DEFAULT_PROFILE).get("exam_date", str(date.today()))) daily_hours = gr.Number(label="Daily Study Hours", value=load_json(PROFILE_FILE, DEFAULT_PROFILE).get("daily_hours", 3)) with gr.Row(): target_grade = gr.Textbox(label="Target Grade", value=load_json(PROFILE_FILE, DEFAULT_PROFILE).get("target_grade", "A+")) study_mode = gr.Dropdown( choices=["Balanced", "Weakness Killer", "Revision Focused", "Exam Crash", "Confidence Builder"], value=load_json(PROFILE_FILE, DEFAULT_PROFILE).get("study_mode", "Balanced"), label="Study Mode" ) save_profile_btn = gr.Button("Save Profile") profile_status = gr.Markdown() save_profile_btn.click( fn=save_profile, inputs=[name, exam_type, exam_date, daily_hours, target_grade, study_mode], outputs=profile_status ) with gr.Tab("Subject Manager"): gr.Markdown("### Add any subject. Example: Physics, Accounting, Marketing, Programming, English, Biology.") with gr.Row(): subject_input = gr.Textbox(label="Subject Name") subject_importance = gr.Slider(1, 10, value=7, step=1, label="Subject Importance") subject_confidence = gr.Slider(1, 10, value=5, step=1, label="Current Confidence") add_subject_btn = gr.Button("Add Subject") subject_status = gr.Markdown() subject_table = gr.Dataframe(value=load_subjects(), label="Subjects", interactive=False) add_subject_btn.click( fn=add_subject, inputs=[subject_input, subject_importance, subject_confidence], outputs=[subject_status, subject_table] ) with gr.Tab("Topic Manager"): gr.Markdown("### Add topics under any subject.") with gr.Row(): topic_subject = gr.Textbox(label="Subject Name") topic_name = gr.Textbox(label="Topic Name") with gr.Row(): topic_difficulty = gr.Slider(1, 10, value=5, step=1, label="Topic Difficulty") topic_importance = gr.Slider(1, 10, value=7, step=1, label="Exam Importance") estimated_hours = gr.Number(label="Estimated Hours Needed", value=2) with gr.Row(): topic_confidence = gr.Slider(1, 10, value=5, step=1, label="Your Confidence") quiz_score = gr.Slider(0, 100, value=0, step=1, label="Current Quiz Score") add_topic_btn = gr.Button("Add Topic") topic_status = gr.Markdown() topic_table = gr.Dataframe(value=load_topics(), label="Topics", interactive=False) add_topic_btn.click( fn=add_topic, inputs=[ topic_subject, topic_name, topic_difficulty, topic_importance, estimated_hours, topic_confidence, quiz_score ], outputs=[topic_status, topic_table] ) with gr.Tab("Smart Study Plan"): gr.Markdown("### Generate today’s adaptive study plan.") max_topics = gr.Slider(1, 6, value=3, step=1, label="Number of Topics for Today") generate_btn = gr.Button("Generate Today’s Plan") plan_md = gr.Markdown() plan_table = gr.Dataframe(label="Recommended Plan", interactive=False) priority_plot = gr.Plot() generate_btn.click( fn=generate_today_plan, inputs=max_topics, outputs=[plan_md, plan_table, priority_plot] ) with gr.Tab("Quiz & Feedback"): gr.Markdown("### After studying, give feedback. This updates the RL memory.") topic_dropdown = gr.Dropdown(choices=get_topic_choices(), label="Select Topic") refresh_topics_btn = gr.Button("Refresh Topic List") action_dropdown = gr.Dropdown(choices=ACTIONS, value="practice_questions", label="Action Taken") completed_checkbox = gr.Checkbox(label="Completed?", value=True) study_minutes = gr.Number(label="Study Minutes", value=60) new_score = gr.Slider(0, 100, value=50, step=1, label="New Quiz Score") difficulty_after = gr.Dropdown(choices=["Easy", "Medium", "Hard"], value="Medium", label="How did the topic feel?") feedback_btn = gr.Button("Submit Feedback") feedback_output = gr.Markdown() updated_topic_table = gr.Dataframe(value=load_topics(), label="Updated Topics", interactive=False) log_table = gr.Dataframe(value=load_logs(), label="Study Logs", interactive=False) feedback_chart = gr.Plot(value=make_subject_progress_chart()) refresh_topics_btn.click( fn=lambda: gr.update(choices=get_topic_choices()), outputs=topic_dropdown ) feedback_btn.click( fn=submit_feedback, inputs=[topic_dropdown, action_dropdown, completed_checkbox, study_minutes, new_score, difficulty_after], outputs=[feedback_output, updated_topic_table, log_table, feedback_chart] ) with gr.Tab("AI Coach"): gr.Markdown("### Ask the study coach. Example: Why should I study Math today?") coach_question = gr.Textbox(label="Your Question", placeholder="Ask about weakness, risk, motivation, or next topic...") coach_btn = gr.Button("Ask Coach") coach_answer = gr.Markdown() coach_btn.click( fn=ai_coach, inputs=coach_question, outputs=coach_answer ) with gr.Tab("Admin / Demo"): gr.Markdown("Use demo data for testing on Hugging Face.") demo_btn = gr.Button("Load Demo Data") clear_btn = gr.Button("Clear All Data") admin_status = gr.Markdown() admin_subjects = gr.Dataframe(value=load_subjects(), label="Subjects") admin_topics = gr.Dataframe(value=load_topics(), label="Topics") admin_logs = gr.Dataframe(value=load_logs(), label="Logs") admin_dashboard = gr.Markdown(get_profile_summary()) admin_chart = gr.Plot(value=make_subject_progress_chart()) demo_btn.click( fn=reset_demo_data, outputs=[admin_status, admin_subjects, admin_topics, admin_logs, admin_dashboard, admin_chart] ) clear_btn.click( fn=clear_all_data, outputs=[admin_status, admin_subjects, admin_topics, admin_logs, admin_dashboard, admin_chart] ) if __name__ == "__main__": demo.launch()