Kerikim commited on
Commit
1ffd314
·
1 Parent(s): 5e3abcf

elkay frontend game xp

Browse files
Files changed (2) hide show
  1. dashboards/student_db.py +25 -28
  2. phase/Student_view/game.py +43 -47
dashboards/student_db.py CHANGED
@@ -14,17 +14,6 @@ def load_css(file_name: str):
14
  except FileNotFoundError:
15
  st.warning("⚠️ Stylesheet not found. Please ensure 'assets/styles.css' exists.")
16
 
17
- def _level_progress(total_xp: int, base: int = 500):
18
- xp = int(max(0, total_xp))
19
- level = max(1, xp // base + 1)
20
- into = xp - (level - 1) * base
21
- need = base
22
- if into == need: # exact boundary
23
- level += 1
24
- into = 0
25
- pct = int(round(100 * into / need)) if need else 0
26
- return level, into, need, pct
27
-
28
  def show_student_dashboard():
29
  # Load CSS
30
  css_path = os.path.join("assets", "styles.css")
@@ -150,29 +139,37 @@ def show_student_dashboard():
150
  st.write("")
151
 
152
  # --- XP Bar ---
153
- # --- XP Bar (live, per-level) ---
154
- total_xp = int(stats.get("xp", 0))
155
- level_api = int(stats.get("level", 1))
156
-
157
- # prefer server fields if present
158
- into = stats.get("into")
159
- need = stats.get("need")
160
-
161
- if isinstance(into, (int, float)) and isinstance(need, (int, float)) and need > 0:
162
- level = level_api
163
- into = int(into)
164
- need = int(need)
165
- pct = min(100, int(round(100 * into / need)))
166
- else:
167
- level, into, need, pct = _level_progress(total_xp)
 
 
 
 
 
 
168
 
169
  st.markdown(
170
  f"""
171
  <div class="xp-card">
172
  <span class="xp-level">Level {level}</span>
173
  <span class="xp-text">{into:,} / {need:,} XP</span>
174
- <div class="xp-bar"><div class="xp-fill" style="width:{pct}%;"></div></div>
175
- <div class="xp-total">Total XP: {total_xp:,}</div>
 
 
176
  </div>
177
  """,
178
  unsafe_allow_html=True
 
14
  except FileNotFoundError:
15
  st.warning("⚠️ Stylesheet not found. Please ensure 'assets/styles.css' exists.")
16
 
 
 
 
 
 
 
 
 
 
 
 
17
  def show_student_dashboard():
18
  # Load CSS
19
  css_path = os.path.join("assets", "styles.css")
 
139
  st.write("")
140
 
141
  # --- XP Bar ---
142
+ # stats already fetched above
143
+ xp = int(stats.get("xp", 0))
144
+ level = int(stats.get("level", 1))
145
+ study_streak = int(stats.get("streak", 0))
146
+
147
+ # prefer server-provided per-level fields
148
+ into = int(stats.get("into", -1))
149
+ need = int(stats.get("need", -1))
150
+
151
+ # fallback if backend hasn't been updated to include into/need yet
152
+ if into < 0 or need <= 0:
153
+ base = 500
154
+ level = max(1, xp // base + 1)
155
+ start = (level - 1) * base
156
+ into = xp - start
157
+ need = base
158
+ if into == need:
159
+ level += 1
160
+ into = 0
161
+
162
+ pct = 0 if need <= 0 else min(100, int(round((into / need) * 100)))
163
 
164
  st.markdown(
165
  f"""
166
  <div class="xp-card">
167
  <span class="xp-level">Level {level}</span>
168
  <span class="xp-text">{into:,} / {need:,} XP</span>
169
+ <div class="xp-bar">
170
+ <div class="xp-fill" style="width: {pct}%;"></div>
171
+ </div>
172
+ <div class="xp-total">Total XP: {xp:,}</div>
173
  </div>
174
  """,
175
  unsafe_allow_html=True
phase/Student_view/game.py CHANGED
@@ -16,16 +16,6 @@ def load_css(file_name: str):
16
 
17
  st.session_state.setdefault("current_game", None)
18
 
19
- def _level_progress(total_xp: int, base: int = 500):
20
- xp = int(max(0, total_xp))
21
- level = max(1, xp // base + 1)
22
- into = xp - (level - 1) * base
23
- need = base
24
- if into == need:
25
- level += 1
26
- into = 0
27
- pct = int(round(100 * into / need)) if need else 0
28
- return level, into, need, pct
29
 
30
  # --- GAME RENDERERS ---
31
  def _render_budget_builder():
@@ -144,40 +134,36 @@ def _load_leaderboard(user_id: int, limit: int = 10) -> list[dict]:
144
  class_id = st.session_state.get("current_class_id")
145
 
146
  # try to pick a class automatically if none set (optional)
147
- if not class_id:
148
  try:
149
- # Prefer backend if DB disabled
150
- if USE_LOCAL_DB and hasattr(dbapi, "list_classes_for_student"):
151
- classes = dbapi.list_classes_for_student(user_id) or []
152
- else:
153
- classes = api.list_classes_for_student(user_id) or []
154
  if classes:
155
  class_id = classes[0]["class_id"]
156
  st.session_state.current_class_id = class_id
157
  except Exception:
158
  pass
159
 
160
- # Fetch rows
161
- rows = []
162
  try:
163
- if USE_LOCAL_DB and hasattr(dbapi, "leaderboard_for_class") and class_id:
164
  rows = dbapi.leaderboard_for_class(class_id, limit=limit) or []
165
- elif USE_LOCAL_DB and hasattr(dbapi, "leaderboard_global"):
166
  rows = dbapi.leaderboard_global(limit=limit) or []
167
  else:
168
- # Backend path: we do not have dedicated leaderboard endpoints,
169
- # so build from class_student_metrics which includes total_xp.
170
- if class_id:
171
- metrics = api.class_student_metrics(class_id) or []
172
- for m in metrics:
173
  rows.append({
174
  "user_id": m.get("student_id"),
175
  "name": m.get("name") or m.get("email") or "Student",
176
- "xp": int(m.get("total_xp", 0)),
177
- "level": int(dbapi.level_from_xp(m.get("total_xp", 0))) if USE_LOCAL_DB else int((int(m.get("total_xp", 0)) // 500) + 1),
178
  })
179
  except Exception as e:
180
  st.warning(f"Leaderboard error: {e}")
 
181
 
182
  # ensure current user present
183
  if not any(r.get("user_id") == user_id for r in rows):
@@ -269,36 +255,45 @@ def show_games():
269
  # pull live XP/level
270
  user_id = st.session_state.user["user_id"]
271
 
272
- # DB if enabled, else backend
273
- try:
274
- if USE_LOCAL_DB and hasattr(dbapi, "user_xp_and_level"):
275
- stats = dbapi.user_xp_and_level(user_id)
276
- else:
277
- stats = api.user_stats(user_id) # /students/{id}/stats
278
- except Exception:
279
- stats = {"xp": int(st.session_state.get("xp", 0)), "level": 1, "streak": 0}
 
280
 
281
  total_xp = int(stats.get("xp", 0))
282
- level_api = int(stats.get("level", 1))
283
- st.session_state.xp = total_xp
284
  st.session_state.streak = int(stats.get("streak", 0))
285
 
 
286
  into = stats.get("into")
287
  need = stats.get("need")
288
 
289
- if isinstance(into, (int, float)) and isinstance(need, (int, float)) and need > 0:
290
- level = level_api
291
- into = int(into)
292
- need = int(need)
293
- pct = min(100, int(round(100 * into / need)))
294
- else:
295
- level, into, need, pct = _level_progress(total_xp)
296
-
297
- st.write(f"Level {level} Experience Points")
 
 
 
 
 
 
 
298
  st.markdown(f"""
299
  <div style="background:#e0e0e0;border-radius:12px;padding:3px;width:100%;">
300
  <div style="
301
- width:{pct}%;
302
  background:linear-gradient(135deg,#22c55e,#059669);
303
  height:24px;border-radius:10px;text-align:right;
304
  color:white;font-weight:bold;padding-right:8px;line-height:24px;">
@@ -310,6 +305,7 @@ def show_games():
310
 
311
 
312
 
 
313
  st.markdown("---")
314
 
315
  # Game list
 
16
 
17
  st.session_state.setdefault("current_game", None)
18
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  # --- GAME RENDERERS ---
21
  def _render_budget_builder():
 
134
  class_id = st.session_state.get("current_class_id")
135
 
136
  # try to pick a class automatically if none set (optional)
137
+ if not class_id and hasattr(dbapi, "list_classes_for_student"):
138
  try:
139
+ classes = dbapi.list_classes_for_student(user_id) or []
 
 
 
 
140
  if classes:
141
  class_id = classes[0]["class_id"]
142
  st.session_state.current_class_id = class_id
143
  except Exception:
144
  pass
145
 
146
+ # fetch rows from DB
 
147
  try:
148
+ if class_id and hasattr(dbapi, "leaderboard_for_class"):
149
  rows = dbapi.leaderboard_for_class(class_id, limit=limit) or []
150
+ elif hasattr(dbapi, "leaderboard_global"):
151
  rows = dbapi.leaderboard_global(limit=limit) or []
152
  else:
153
+ # fallback using existing function if needed
154
+ rows = []
155
+ if class_id and hasattr(dbapi, "class_student_metrics"):
156
+ for m in dbapi.class_student_metrics(class_id) or []:
157
+ xp = int(m.get("total_xp", 0))
158
  rows.append({
159
  "user_id": m.get("student_id"),
160
  "name": m.get("name") or m.get("email") or "Student",
161
+ "xp": xp,
162
+ "level": dbapi.level_from_xp(xp),
163
  })
164
  except Exception as e:
165
  st.warning(f"Leaderboard error: {e}")
166
+ rows = []
167
 
168
  # ensure current user present
169
  if not any(r.get("user_id") == user_id for r in rows):
 
255
  # pull live XP/level
256
  user_id = st.session_state.user["user_id"]
257
 
258
+ # Prefer local DB only if enabled. Otherwise call backend.
259
+ if USE_LOCAL_DB and hasattr(dbapi, "user_xp_and_level"):
260
+ stats = dbapi.user_xp_and_level(user_id) # {'xp', 'level', 'streak', maybe 'into','need'}
261
+ else:
262
+ try:
263
+ stats = api.user_stats(user_id) # backend /students/{id}/stats
264
+ except Exception as e:
265
+ # hard fallback so the page still renders
266
+ stats = {"xp": int(st.session_state.get("xp", 0)), "level": 1, "streak": 0}
267
 
268
  total_xp = int(stats.get("xp", 0))
269
+ level = int(stats.get("level", 1))
270
+ st.session_state.xp = total_xp
271
  st.session_state.streak = int(stats.get("streak", 0))
272
 
273
+ # Use server-provided per-level values when available
274
  into = stats.get("into")
275
  need = stats.get("need")
276
 
277
+ # Fallback if backend has not been updated yet
278
+ if into is None or need in (None, 0):
279
+ base = 500
280
+ level = max(1, total_xp // base + 1)
281
+ start = (level - 1) * base
282
+ into = total_xp - start
283
+ need = base
284
+ if into == need:
285
+ level += 1
286
+ into = 0
287
+
288
+ into = int(max(0, into))
289
+ need = int(max(1, need))
290
+ progress_pct = min(100, int(round((into / need) * 100)))
291
+
292
+ st.write(f"Level {level} Experience Points")
293
  st.markdown(f"""
294
  <div style="background:#e0e0e0;border-radius:12px;padding:3px;width:100%;">
295
  <div style="
296
+ width:{progress_pct}%;
297
  background:linear-gradient(135deg,#22c55e,#059669);
298
  height:24px;border-radius:10px;text-align:right;
299
  color:white;font-weight:bold;padding-right:8px;line-height:24px;">
 
305
 
306
 
307
 
308
+
309
  st.markdown("---")
310
 
311
  # Game list