| 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 |
|
|
| USE_LOCAL_DB = os.getenv("DISABLE_DB", "1") != "1" |
|
|
| |
| 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(): |
| |
| css_path = os.path.join("assets", "styles.css") |
| load_css(css_path) |
|
|
| |
| user = st.session_state.user |
| name = user["name"] |
| student_id = user["user_id"] |
|
|
| |
| |
| if USE_LOCAL_DB and hasattr(dbapi, "user_xp_and_level"): |
| stats = dbapi.user_xp_and_level(student_id) |
| else: |
| |
| 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)) |
|
|
| |
| |
|
|
| |
| 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) |
|
|
| |
| 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 = [] |
|
|
| |
| 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 = [ |
| {"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}, |
| ] |
|
|
| |
| 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("") |
|
|
| |
| actions = [ |
| ("π Start a Lesson", "Lessons"), |
| ("π Attempt a Quiz", "Quiz"), |
| ("π¬ Talk to AI Tutor", "Chatbot"), |
| ] |
|
|
| |
| cols = st.columns([1, 2, 2, 2, 1]) |
| for i, (label, page) in enumerate(actions): |
| with cols[i+1]: |
| if st.button(label, key=f"action_{i}"): |
| st.session_state.current_page = page |
| st.rerun() |
|
|
| st.write("") |
|
|
| |
| 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 = int(stats.get("xp", 0)) |
| level = int(stats.get("level", 1)) |
| study_streak = int(stats.get("streak", 0)) |
|
|
| |
| into = int(stats.get("into", -1)) |
| need = int(stats.get("need", -1)) |
|
|
| |
| 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) |
| 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 |
| ) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| 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("---") |
|
|
| |
| col1, col2 = st.columns(2) |
|
|
| def _progress_value(v): |
| try: |
| f = float(v) |
| except Exception: |
| return 0.0 |
| |
| 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']}") |
|
|