Study-Pilot-Ai / app.py
Learnix-AI-Lab's picture
Upload 2 files
084f1e7 verified
Raw History Blame Contribute Delete
33.7 kB
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()