File size: 7,090 Bytes
0aa6283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# 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")