Download phase/Teacher_view/classmanage.py from FinEDProj/FInFront: direct link, hf CLI and curl.
- Browser
- Download file 7.09 kB
-
https://huggingface.co/spaces/FinEDProj/FInFront/resolve/main/phase/Teacher_view/classmanage.py
- Command line
-
hf download hf://spaces/FinEDProj/FInFront/phase/Teacher_view/classmanage.py
-
curl -L -o classmanage.py https://huggingface.co/spaces/FinEDProj/FInFront/resolve/main/phase/Teacher_view/classmanage.py
7.09 kB
| # phase/Teacher_view/classmanage.py | |
| import os | |
| import streamlit as st | |
| from utils import db as dbapi | |
| import utils.api as api # backend Space client | |
| # When DISABLE_DB=1 (default), skip direct MySQL and use backend APIs | |
| USE_LOCAL_DB = os.getenv("DISABLE_DB", "1") != "1" | |
| def _metric_card(label: str, value: str, caption: str = ""): | |
| st.markdown( | |
| f""" | |
| <div class="metric-card"> | |
| <div class="metric-value">{value}</div> | |
| <div class="metric-label">{label}</div> | |
| <div class="metric-caption">{caption}</div> | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| def _prefer_db(db_name: str, api_func, default, *args, **kwargs): | |
| """ | |
| Try local DB function if enabled & present; else call backend API; else return default. | |
| """ | |
| if USE_LOCAL_DB and hasattr(dbapi, db_name): | |
| try: | |
| return getattr(dbapi, db_name)(*args, **kwargs) | |
| except Exception as e: | |
| st.warning(f"DB call {db_name} failed; falling back to backend. ({e})") | |
| try: | |
| return api_func(*args, **kwargs) | |
| except Exception as e: | |
| st.error(f"Backend call failed: {e}") | |
| return default | |
| def show_page(): | |
| user = st.session_state.user | |
| teacher_id = user["user_id"] | |
| st.title("π Classroom Management") | |
| st.caption("Manage all your classrooms and students") | |
| # -------- Create Classroom -------- | |
| with st.expander("β Create Classroom", expanded=False): | |
| new_name = st.text_input("Classroom Name", key="new_classroom_name") | |
| if st.button("Create Classroom"): | |
| name = new_name.strip() | |
| if not name: | |
| st.error("Enter a real name, not whitespace.") | |
| else: | |
| out = _prefer_db( | |
| "create_class", | |
| lambda tid, n: api.create_class(tid, n), | |
| None, | |
| teacher_id, # positional arg | |
| name, # positional arg | |
| ) | |
| if out: | |
| st.session_state.selected_class_id = out.get("class_id") or out.get("id") | |
| st.success(f'Classroom "{name}" created with code: {out.get("code","β")}') | |
| st.rerun() | |
| else: | |
| st.error("Could not create classroom (no response).") | |
| # -------- Load classes for this teacher -------- | |
| classes = _prefer_db( | |
| "list_classes_by_teacher", | |
| lambda tid: api.list_classes_by_teacher(tid), | |
| [], | |
| teacher_id, # positional | |
| ) | |
| if not classes: | |
| st.info("No classrooms yet. Create one above, then share the code.") | |
| return | |
| # Picker | |
| st.subheader("Your Classrooms") | |
| options = {f"{c.get('name','(unnamed)')} (Code: {c.get('code','')})": c for c in classes} | |
| selected_label = st.selectbox("Select a classroom", list(options.keys())) | |
| selected = options[selected_label] | |
| class_id = selected.get("class_id") or selected.get("id") | |
| st.markdown("---") | |
| st.header(selected.get("name", "Classroom")) | |
| # -------- Code stripe -------- | |
| st.subheader("Class Code") | |
| c1, c2, c3 = st.columns([3, 1, 1]) | |
| with c1: | |
| st.markdown(f"**`{selected.get('code', 'UNKNOWN')}`**") | |
| with c2: | |
| if st.button("π Copy Code"): | |
| st.toast("Code is shown above. Copy it.") | |
| with c3: | |
| st.button("ποΈ Delete Class", disabled=True, help="Soft-delete coming later") | |
| # -------- Tabs -------- | |
| tab_students, tab_content, tab_analytics = st.tabs(["π₯ Students", "π Content", "π Analytics"]) | |
| # ============== Students tab ============== | |
| with tab_students: | |
| q = st.text_input("Search students by name or email", "") | |
| roster = _prefer_db( | |
| "list_students_in_class", | |
| lambda cid: api.list_students_in_class(cid), | |
| [], | |
| class_id, # positional | |
| ) | |
| # simple filter | |
| if q.strip(): | |
| ql = q.lower() | |
| roster = [r for r in roster if ql in (r.get("name","").lower()) or ql in (r.get("email","").lower())] | |
| st.caption(f"{len(roster)} Students Found") | |
| if not roster: | |
| st.info("No students in this class yet.") | |
| else: | |
| for s in roster: | |
| st.subheader(f"π€ {s.get('name','(unknown)')}") | |
| st.caption(s.get("email","β")) | |
| joined = s.get("joined_at") or s.get("created_at") | |
| st.caption(f"π Joined: {str(joined)[:10] if joined else 'β'}") | |
| st.progress(0.0) # placeholder bar | |
| cols = st.columns(3) | |
| level_slug = (s.get("level_slug") or s.get("level") or "beginner") | |
| try: | |
| level_label = level_slug.capitalize() if isinstance(level_slug, str) else str(level_slug) | |
| except Exception: | |
| level_label = "β" | |
| cols[0].metric("β Level", level_label) | |
| cols[1].metric("π Avg Score", "β") | |
| cols[2].metric("π₯ Streak", "β") | |
| st.markdown("---") | |
| # ============== Content tab ============== | |
| with tab_content: | |
| counts = _prefer_db( | |
| "class_content_counts", | |
| lambda cid: api.class_content_counts(cid), | |
| {"lessons": 0, "quizzes": 0}, | |
| class_id, # positional | |
| ) | |
| left, right = st.columns(2) | |
| with left: | |
| _metric_card("π Custom Lessons", str(counts.get("lessons", 0)), "Lessons created for this classroom") | |
| with right: | |
| _metric_card("π Custom Quizzes", str(counts.get("quizzes", 0)), "Quizzes created for this classroom") | |
| assigs = _prefer_db( | |
| "list_class_assignments", | |
| lambda cid: api.list_class_assignments(cid), | |
| [], | |
| class_id, # positional | |
| ) | |
| if assigs: | |
| st.markdown("#### Assigned items") | |
| for a in assigs: | |
| has_quiz = " + Quiz" if a.get("quiz_id") else "" | |
| st.markdown(f"- **{a.get('title','Untitled')}** Β· {a.get('subject','β')} Β· {a.get('level','β')}{has_quiz}") | |
| # ============== Analytics tab ============== | |
| with tab_analytics: | |
| stats = _prefer_db( | |
| "class_analytics", | |
| lambda cid: api.class_analytics(cid), | |
| {"class_avg": 0.0, "total_xp": 0, "lessons_completed": 0}, | |
| class_id, # positional | |
| ) | |
| class_avg_pct = round(float(stats.get("class_avg", 0)) * 100) if stats.get("class_avg") is not None else 0 | |
| total_xp = stats.get("total_xp", 0) | |
| lessons_completed = stats.get("lessons_completed", 0) | |
| g1, g2, g3 = st.columns(3) | |
| with g1: | |
| _metric_card("π Class Average", f"{class_avg_pct}%", "Average quiz performance") | |
| with g2: | |
| _metric_card("πͺ Total XP", f"{total_xp}", "Combined XP earned") | |
| with g3: | |
| _metric_card("π Lessons Completed", f"{lessons_completed}", "Total lessons completed") | |