FInFront / dashboards /student_db.py
lanna_lalala;-
added folders
0aa6283
import os
import streamlit as st
from phase.Student_view import chatbot, lesson, quiz
from utils import db as dbapi
import utils.api as api # <-- backend Space client
USE_LOCAL_DB = os.getenv("DISABLE_DB", "1") != "1"
# --- Load external CSS ---
def load_css(file_name: str):
try:
with open(file_name, "r", encoding="utf-8") as f:
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
except FileNotFoundError:
st.warning("⚠️ Stylesheet not found. Please ensure 'assets/styles.css' exists.")
def show_student_dashboard():
# Load CSS
css_path = os.path.join("assets", "styles.css")
load_css(css_path)
# Current user
user = st.session_state.user
name = user["name"]
student_id = user["user_id"]
# --- Real metrics from DB ---
# Requires helper funcs in utils/db.py: user_xp_and_level, recent_lessons_for_student, list_assignments_for_student
if USE_LOCAL_DB and hasattr(dbapi, "user_xp_and_level"):
stats = dbapi.user_xp_and_level(student_id)
else:
# Try backend; fall back to defaults if not available yet
try:
stats = api.user_stats(student_id)
except Exception:
stats = {"xp": 0, "level": 1, "streak": 0}
xp = int(stats.get("xp", 0))
level = int(stats.get("level", 1))
study_streak = int(stats.get("streak", 0))
# # Cap for the visual bar
# max_xp = max(500, ((xp // 500) + 1) * 500)
# Assignments for β€œMy Work”
if USE_LOCAL_DB and hasattr(dbapi, "list_assignments_for_student"):
rows = dbapi.list_assignments_for_student(student_id)
else:
try:
rows = api.list_assignments_for_student(student_id)
except Exception:
rows = []
def _pct_from_row(r: dict):
sp = r.get("score_pct")
if sp is not None:
try:
return int(round(float(sp)))
except Exception:
pass
s, t = r.get("score"), r.get("total")
if s is not None and t not in (None, 0):
try:
return int(round((float(s) / float(t)) * 100))
except Exception:
return None
return None
if USE_LOCAL_DB and hasattr(dbapi, "student_quiz_average"):
quiz_score = dbapi.student_quiz_average(student_id)
else:
try:
quiz_score = api.student_quiz_average(student_id)
except Exception:
quiz_score = 0
lessons_completed = sum(1 for r in rows if r.get("status") == "completed" or _pct_from_row(r) == 100)
total_lessons = len(rows)
# Recent lessons assigned to this student
if USE_LOCAL_DB and hasattr(dbapi, "recent_lessons_for_student"):
recent_lessons = dbapi.recent_lessons_for_student(student_id, limit=5)
else:
try:
recent_lessons = api.recent_lessons_for_student(student_id, limit=5)
except Exception:
recent_lessons = []
# Daily Challenge derived from real data
challenge_difficulty = "Easy" if level < 3 else ("Medium" if level < 6 else "Hard")
challenge_title = "Complete 1 quiz with 80%+"
challenge_desc = "Prove you remember yesterday's key points."
challenge_progress = 100 if quiz_score >= 80 else 0
reward = "+50 XP"
time_left = "Ends 11:59 PM"
# Achievements from real data
achievements = [
{"title": "First Steps", "desc": "Complete your first lesson", "earned": lessons_completed > 0},
{"title": "Quiz Whiz", "desc": "Score 80%+ on any quiz", "earned": quiz_score >= 80},
{"title": "On a Roll", "desc": "Study 3 days in a row", "earned": study_streak >= 3},
{"title": "Consistency", "desc": "Finish 5 assignments", "earned": total_lessons >= 5 and lessons_completed >= 5},
]
# --- Welcome Card ---
st.markdown(
f"""
<div class="welcome-card">
<h2>Welcome back, {name}!</h2>
<p style="font-size: 20px;">{"Ready to continue your financial journey?" if lessons_completed > 0 else "Start your financial journey."}</p>
</div>
""",
unsafe_allow_html=True
)
st.write("")
# --- Quick Action Buttons ---
actions = [
("πŸ“š Start a Lesson", "Lessons"),
("πŸ“ Attempt a Quiz", "Quiz"),
("πŸ’¬ Talk to AI Tutor", "Chatbot"),
]
# 5 columns: spacer, button, button, button, spacer
cols = st.columns([1, 2, 2, 2, 1])
for i, (label, page) in enumerate(actions):
with cols[i+1]: # skip the left spacer
if st.button(label, key=f"action_{i}"):
st.session_state.current_page = page
st.rerun()
st.write("")
# --- Progress Summary Cards ---
progress_cols = st.columns(3)
progress_cols[0].metric("πŸ“˜ Lessons Completed", f"{lessons_completed}/{total_lessons}")
progress_cols[1].metric("πŸ“Š Quiz Score", f"{quiz_score}/100")
progress_cols[2].metric("πŸ”₯ Study Streak", f"{study_streak} days")
st.write("")
# --- XP Bar ---
# stats already fetched above
xp = int(stats.get("xp", 0))
level = int(stats.get("level", 1))
study_streak = int(stats.get("streak", 0))
# prefer server-provided per-level fields
into = int(stats.get("into", -1))
need = int(stats.get("need", -1))
# fallback if backend hasn't been updated to include into/need yet
if into < 0 or need <= 0:
base = 500
level = max(1, xp // base + 1)
start = (level - 1) * base
into = xp - start
need = base
if into == need:
level += 1
into = 0
cap = max(500, ((xp // 500) + 1) * 500) # next threshold
pct = 0 if cap <= 0 else min(100, int(round(100 * xp / cap)))
st.markdown(
f"""
<div class="xp-card">
<span class="xp-level">Level {level}</span>
<span class="xp-text">{xp:,} / {cap:,} XP</span>
<div class="xp-bar">
<div class="xp-fill" style="width: {pct}%;"></div>
</div>
<div class="xp-total">Total XP: {xp:,}</div>
</div>
""",
unsafe_allow_html=True
)
# pct = 0 if max_xp <= 0 else min(100, int(round((xp / max_xp) * 100)))
# st.markdown(
# f"""
# <div class="xp-card">
# <span class="xp-level">Level {level}</span>
# <span class="xp-text">{xp} / {max_xp} XP</span>
# <div class="xp-bar">
# <div class="xp-fill" style="width: {pct}%;"></div>
# </div>
# </div>
# """,
# unsafe_allow_html=True
# )
# st.write("")
# --- My Assignments (from DB) ---
st.markdown("---")
st.subheader("πŸ“˜ My Work")
if not rows:
st.info("No assignments yet. Ask your teacher to assign a lesson.")
else:
for a in rows:
title = a.get("title", "Untitled")
subj = a.get("subject", "General")
lvl = a.get("level", "Beginner")
status = a.get("status", "not_started")
due = a.get("due_at")
due_txt = f" Β· Due {str(due)[:10]}" if due else ""
st.markdown(f"**{title}** Β· {subj} Β· {lvl}{due_txt}")
st.caption(f"Status: {status} Β· Resume at section {a.get('current_pos', 1)}")
st.markdown("---")
# --- Recent Lessons & Achievements ---
col1, col2 = st.columns(2)
def _progress_value(v):
try:
f = float(v)
except Exception:
return 0.0
# streamlit accepts 0–1 float; if someone passes 0–100, scale it
return max(0.0, min(1.0, f if f <= 1.0 else f / 100.0))
with col1:
st.subheader("πŸ“– Recent Lessons")
st.caption("Continue where you left off")
if not recent_lessons:
st.info("No recent lessons yet.")
else:
for lesson in recent_lessons:
prog = lesson.get("progress", 0)
st.progress(_progress_value(prog))
status = "βœ… Complete" if (isinstance(prog, (int, float)) and prog >= 100) else f"{int(prog)}% complete"
st.write(f"**{lesson.get('title','Untitled Lesson')}** β€” {status}")
with col2:
st.subheader("πŸ† Achievements")
st.caption("Your learning milestones")
for ach in achievements:
if ach["earned"]:
st.success(f"βœ” {ach['title']} β€” {ach['desc']}")
else:
st.info(f"πŸ”’ {ach['title']} β€” {ach['desc']}")