Kerikim commited on
Commit
44b5561
·
1 Parent(s): bee3a77

elkay frontend game leaderboard

Browse files
Files changed (1) hide show
  1. phase/Student_view/game.py +88 -43
phase/Student_view/game.py CHANGED
@@ -125,60 +125,102 @@ def render_leaderboard(leaderboard):
125
 
126
  def _load_leaderboard(user_id: int, limit: int = 10) -> list[dict]:
127
  """
128
- Shape returned fits render_leaderboard:
129
  [{"rank": int|"You", "name": str, "level": int, "xp": int, "user_id": int}, ...]
130
- Prefers class leaderboard if current_class_id is set; else global.
131
- Ensures current user is included and labeled "You".
132
  """
133
  you_name = (st.session_state.get("user") or {}).get("name") or "You"
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):
170
- try:
171
- stats = dbapi.user_xp_and_level(user_id) or {}
172
- rows.append({
173
- "user_id": user_id,
174
- "name": you_name,
175
- "xp": int(stats.get("xp", 0)),
176
- "level": int(stats.get("level", 1)),
177
- })
178
- except Exception:
179
- rows.append({"user_id": user_id, "name": you_name, "xp": 0, "level": 1})
180
-
181
- # sort, rank, and mark "You"
 
 
 
 
182
  rows.sort(key=lambda r: int(r.get("xp", 0)), reverse=True)
183
  ranked = []
184
  for i, r in enumerate(rows, start=1):
@@ -189,17 +231,20 @@ def _load_leaderboard(user_id: int, limit: int = 10) -> list[dict]:
189
  "level": int(r["level"]),
190
  "xp": int(r["xp"]),
191
  })
 
 
192
  for r in ranked:
193
  if r["user_id"] == user_id:
194
  r["rank"] = "You"
195
  break
196
 
197
- # "You" first visually, then others by rank
198
  you = [r for r in ranked if r["rank"] == "You"]
199
  others = [r for r in ranked if r["rank"] != "You"]
200
  return (you + others)[:limit]
201
 
202
 
 
203
  # --- MAIN GAMES HUB & ROUTER ---
204
  def show_games():
205
  load_css(os.path.join("assets", "styles.css"))
 
125
 
126
  def _load_leaderboard(user_id: int, limit: int = 10) -> list[dict]:
127
  """
128
+ Returns rows shaped for render_leaderboard():
129
  [{"rank": int|"You", "name": str, "level": int, "xp": int, "user_id": int}, ...]
130
+ - If DISABLE_DB=1, fetch from backend via utils.api.
131
+ - If local DB is enabled, use utils.db.
132
  """
133
  you_name = (st.session_state.get("user") or {}).get("name") or "You"
134
  class_id = st.session_state.get("current_class_id")
135
+ rows: list[dict] = []
136
 
 
 
 
 
 
 
 
 
 
 
 
137
  try:
138
+ if USE_LOCAL_DB:
139
+ # Local DB path
140
+ if not class_id and hasattr(dbapi, "list_classes_for_student"):
141
+ classes = dbapi.list_classes_for_student(user_id) or []
142
+ if classes:
143
+ class_id = classes[0]["class_id"]
144
+ st.session_state.current_class_id = class_id
145
+
146
+ if class_id and hasattr(dbapi, "leaderboard_for_class"):
147
+ rows = dbapi.leaderboard_for_class(class_id, limit=limit) or []
148
+ elif hasattr(dbapi, "leaderboard_global"):
149
+ rows = dbapi.leaderboard_global(limit=limit) or []
150
+ elif class_id and hasattr(dbapi, "class_student_metrics"):
151
+ # Build from metrics if no leaderboard helper
152
+ metrics = dbapi.class_student_metrics(class_id) or []
153
+ rows = [
154
+ {
155
+ "user_id": m.get("student_id"),
156
+ "name": m.get("name") or m.get("email") or "Student",
157
+ "xp": int(m.get("total_xp", 0)),
158
+ "level": dbapi.level_from_xp(int(m.get("total_xp", 0))),
159
+ }
160
+ for m in metrics
161
+ ]
162
  else:
163
+ # Backend API path
164
+ if not class_id:
165
+ try:
166
+ classes = api.list_classes_for_student(user_id) or []
167
+ except Exception:
168
+ classes = []
169
+ if classes:
170
+ class_id = classes[0].get("class_id")
171
+ st.session_state.current_class_id = class_id
172
+
173
+ if class_id:
174
+ # Use per-class metrics from backend and derive leaderboard
175
+ try:
176
+ metrics = api.class_student_metrics(class_id) or []
177
+ except Exception:
178
+ metrics = []
179
+ rows = [
180
+ {
181
  "user_id": m.get("student_id"),
182
  "name": m.get("name") or m.get("email") or "Student",
183
+ "xp": int(m.get("total_xp", 0)),
184
+ # if backend does not send level, compute from XP
185
+ "level": (int(m.get("level")) if "level" in m
186
+ else 1 + int(m.get("total_xp", 0)) // 500),
187
+ }
188
+ for m in metrics
189
+ ]
190
+ else:
191
+ # No class. At least show the current user by calling /students/{id}/stats
192
+ try:
193
+ stats = api.user_stats(user_id) or {}
194
+ except Exception:
195
+ stats = {}
196
+ rows = [{
197
+ "user_id": user_id,
198
+ "name": you_name,
199
+ "xp": int(stats.get("xp", 0)),
200
+ "level": int(stats.get("level", 1)),
201
+ }]
202
+ except Exception:
203
+ # Silent fallback, do not spam a yellow warning box
204
  rows = []
205
 
206
+ # Ensure current user is present
207
  if not any(r.get("user_id") == user_id for r in rows):
208
+ # pull stats from whichever path is available
209
+ if USE_LOCAL_DB:
210
+ try:
211
+ s = dbapi.user_xp_and_level(user_id) or {}
212
+ you_xp = int(s.get("xp", 0)); you_lvl = int(s.get("level", 1))
213
+ except Exception:
214
+ you_xp, you_lvl = 0, 1
215
+ else:
216
+ try:
217
+ s = api.user_stats(user_id) or {}
218
+ you_xp = int(s.get("xp", 0)); you_lvl = int(s.get("level", 1))
219
+ except Exception:
220
+ you_xp, you_lvl = 0, 1
221
+ rows.append({"user_id": user_id, "name": you_name, "xp": you_xp, "level": you_lvl})
222
+
223
+ # Sort by XP desc and rank
224
  rows.sort(key=lambda r: int(r.get("xp", 0)), reverse=True)
225
  ranked = []
226
  for i, r in enumerate(rows, start=1):
 
231
  "level": int(r["level"]),
232
  "xp": int(r["xp"]),
233
  })
234
+
235
+ # Mark "You"
236
  for r in ranked:
237
  if r["user_id"] == user_id:
238
  r["rank"] = "You"
239
  break
240
 
241
+ # Put "You" at the top visually
242
  you = [r for r in ranked if r["rank"] == "You"]
243
  others = [r for r in ranked if r["rank"] != "You"]
244
  return (you + others)[:limit]
245
 
246
 
247
+
248
  # --- MAIN GAMES HUB & ROUTER ---
249
  def show_games():
250
  load_css(os.path.join("assets", "styles.css"))