elkay frontend xp
Browse files- dashboards/student_db.py +40 -5
- utils/db.py +22 -10
dashboards/student_db.py
CHANGED
|
@@ -39,8 +39,8 @@ def show_student_dashboard():
|
|
| 39 |
level = int(stats.get("level", 1))
|
| 40 |
study_streak = int(stats.get("streak", 0))
|
| 41 |
|
| 42 |
-
# Cap for the visual bar
|
| 43 |
-
max_xp = max(500, ((xp // 500) + 1) * 500)
|
| 44 |
|
| 45 |
# Assignments for “My Work”
|
| 46 |
if USE_LOCAL_DB and hasattr(dbapi, "list_assignments_for_student"):
|
|
@@ -156,20 +156,55 @@ def show_student_dashboard():
|
|
| 156 |
st.markdown("---")
|
| 157 |
|
| 158 |
# --- XP Bar ---
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
st.markdown(
|
| 161 |
f"""
|
| 162 |
<div class="xp-card">
|
| 163 |
<span class="xp-level">Level {level}</span>
|
| 164 |
-
<span class="xp-text">{
|
| 165 |
<div class="xp-bar">
|
| 166 |
<div class="xp-fill" style="width: {pct}%;"></div>
|
| 167 |
</div>
|
|
|
|
| 168 |
</div>
|
| 169 |
""",
|
| 170 |
unsafe_allow_html=True
|
| 171 |
)
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
|
| 175 |
# --- Recent Lessons & Achievements ---
|
|
|
|
| 39 |
level = int(stats.get("level", 1))
|
| 40 |
study_streak = int(stats.get("streak", 0))
|
| 41 |
|
| 42 |
+
# # Cap for the visual bar
|
| 43 |
+
# max_xp = max(500, ((xp // 500) + 1) * 500)
|
| 44 |
|
| 45 |
# Assignments for “My Work”
|
| 46 |
if USE_LOCAL_DB and hasattr(dbapi, "list_assignments_for_student"):
|
|
|
|
| 156 |
st.markdown("---")
|
| 157 |
|
| 158 |
# --- XP Bar ---
|
| 159 |
+
# stats already fetched above
|
| 160 |
+
xp = int(stats.get("xp", 0))
|
| 161 |
+
level = int(stats.get("level", 1))
|
| 162 |
+
study_streak = int(stats.get("streak", 0))
|
| 163 |
+
|
| 164 |
+
# prefer server-provided per-level fields
|
| 165 |
+
into = int(stats.get("into", -1))
|
| 166 |
+
need = int(stats.get("need", -1))
|
| 167 |
+
|
| 168 |
+
# fallback if backend hasn't been updated to include into/need yet
|
| 169 |
+
if into < 0 or need <= 0:
|
| 170 |
+
base = 500
|
| 171 |
+
level = max(1, xp // base + 1)
|
| 172 |
+
start = (level - 1) * base
|
| 173 |
+
into = xp - start
|
| 174 |
+
need = base
|
| 175 |
+
if into == need:
|
| 176 |
+
level += 1
|
| 177 |
+
into = 0
|
| 178 |
+
|
| 179 |
+
pct = 0 if need <= 0 else min(100, int(round((into / need) * 100)))
|
| 180 |
+
|
| 181 |
st.markdown(
|
| 182 |
f"""
|
| 183 |
<div class="xp-card">
|
| 184 |
<span class="xp-level">Level {level}</span>
|
| 185 |
+
<span class="xp-text">{into:,} / {need:,} XP</span>
|
| 186 |
<div class="xp-bar">
|
| 187 |
<div class="xp-fill" style="width: {pct}%;"></div>
|
| 188 |
</div>
|
| 189 |
+
<div class="xp-total">Total XP: {xp:,}</div>
|
| 190 |
</div>
|
| 191 |
""",
|
| 192 |
unsafe_allow_html=True
|
| 193 |
)
|
| 194 |
+
# pct = 0 if max_xp <= 0 else min(100, int(round((xp / max_xp) * 100)))
|
| 195 |
+
# st.markdown(
|
| 196 |
+
# f"""
|
| 197 |
+
# <div class="xp-card">
|
| 198 |
+
# <span class="xp-level">Level {level}</span>
|
| 199 |
+
# <span class="xp-text">{xp} / {max_xp} XP</span>
|
| 200 |
+
# <div class="xp-bar">
|
| 201 |
+
# <div class="xp-fill" style="width: {pct}%;"></div>
|
| 202 |
+
# </div>
|
| 203 |
+
# </div>
|
| 204 |
+
# """,
|
| 205 |
+
# unsafe_allow_html=True
|
| 206 |
+
# )
|
| 207 |
+
# st.write("")
|
| 208 |
|
| 209 |
|
| 210 |
# --- Recent Lessons & Achievements ---
|
utils/db.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
# utils/db.py (top of file)
|
| 2 |
-
# utils/db.py (top of file)
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
import certifi
|
|
@@ -1015,17 +1014,30 @@ def _count_lessons(teacher_id:int):
|
|
| 1015 |
|
| 1016 |
|
| 1017 |
# --- XP and streak helpers ---
|
| 1018 |
-
def user_xp_and_level(user_id:int):
|
| 1019 |
with cursor() as cur:
|
| 1020 |
cur.execute("SELECT COALESCE(SUM(delta),0) AS xp FROM xp_log WHERE user_id=%s", (user_id,))
|
| 1021 |
-
xp = (cur.fetchone() or {"xp": 0})["xp"]
|
| 1022 |
-
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
-
|
| 1027 |
-
|
| 1028 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1029 |
|
| 1030 |
def recent_lessons_for_student(user_id:int, limit:int=5):
|
| 1031 |
with cursor() as cur:
|
|
|
|
| 1 |
# utils/db.py (top of file)
|
|
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import certifi
|
|
|
|
| 1014 |
|
| 1015 |
|
| 1016 |
# --- XP and streak helpers ---
|
| 1017 |
+
def user_xp_and_level(user_id: int, base: int = 500):
|
| 1018 |
with cursor() as cur:
|
| 1019 |
cur.execute("SELECT COALESCE(SUM(delta),0) AS xp FROM xp_log WHERE user_id=%s", (user_id,))
|
| 1020 |
+
xp = int((cur.fetchone() or {"xp": 0})["xp"])
|
| 1021 |
+
cur.execute("SELECT COALESCE(days,0) AS days FROM streaks WHERE user_id=%s", (user_id,))
|
| 1022 |
+
streak = int((cur.fetchone() or {"days": 0})["days"])
|
| 1023 |
+
|
| 1024 |
+
# level math
|
| 1025 |
+
level = max(1, xp // base + 1)
|
| 1026 |
+
start_of_level = (level - 1) * base
|
| 1027 |
+
into = xp - start_of_level
|
| 1028 |
+
need = base
|
| 1029 |
+
# exact boundary should flip level and reset progress
|
| 1030 |
+
if into == need:
|
| 1031 |
+
level += 1
|
| 1032 |
+
into = 0
|
| 1033 |
+
|
| 1034 |
+
return {
|
| 1035 |
+
"xp": xp, # lifetime XP from the DB
|
| 1036 |
+
"level": level, # current level
|
| 1037 |
+
"into": into, # XP inside this level
|
| 1038 |
+
"need": need, # XP needed to reach next level
|
| 1039 |
+
"streak": streak,
|
| 1040 |
+
}
|
| 1041 |
|
| 1042 |
def recent_lessons_for_student(user_id:int, limit:int=5):
|
| 1043 |
with cursor() as cur:
|