Spaces:
Running
Running
Update app.py
#89
by
Muthuraja18
- opened
app.py
CHANGED
|
@@ -415,6 +415,45 @@ def unified_push_leaderboard(row):
|
|
| 415 |
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
|
| 416 |
df.to_csv(LEADERBOARD_FILE, index=False)
|
| 417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
# ----------------- Session & Presence helpers -----------------
|
| 419 |
def now_iso():
|
| 420 |
return datetime.utcnow().isoformat()
|
|
@@ -528,7 +567,16 @@ def compute_winners(game_id):
|
|
| 528 |
|
| 529 |
return df.head(3).to_dict(orient="records")
|
| 530 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 531 |
# ----------------- Create Game Page -----------------
|
|
|
|
| 532 |
def create_game_page():
|
| 533 |
st.header("Create Game")
|
| 534 |
|
|
@@ -695,75 +743,88 @@ def home_page():
|
|
| 695 |
st.header("Home")
|
| 696 |
st.write("Create games, invite friends, play and climb the leaderboard.")
|
| 697 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 698 |
# Show last score
|
| 699 |
if st.session_state.get('last_score') is not None:
|
| 700 |
-
st.success(
|
|
|
|
|
|
|
|
|
|
| 701 |
|
| 702 |
-
# Load games
|
| 703 |
games = unified_get("games") or {}
|
| 704 |
-
players_map = unified_get("players") or {}
|
| 705 |
|
| 706 |
st.subheader("Recent games")
|
|
|
|
| 707 |
for g in sorted(games.values(), key=lambda x: x.get("created_at", ""), reverse=True)[:10]:
|
| 708 |
gid = g.get("game_id")
|
|
|
|
|
|
|
| 709 |
st.markdown(f"### ๐ฎ Game: **{gid}** {'(Closed)' if g.get('closed') else ''}")
|
| 710 |
st.write(f"Host: {g.get('host')} โ Topics: {', '.join(g.get('topics', []))}")
|
| 711 |
st.write(f"Created: {g.get('created_at')}")
|
| 712 |
|
| 713 |
-
#
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
)
|
|
|
|
|
|
|
| 731 |
|
| 732 |
-
#
|
| 733 |
if players_here:
|
| 734 |
st.markdown("**๐ฅ Player Status**")
|
| 735 |
for uname_p, info in players_here.items():
|
| 736 |
-
status = "โ
Submitted" if info.get(
|
| 737 |
st.write(f"{info.get('avatar','๐ฎ')} **{uname_p}** โ {status}")
|
| 738 |
|
| 739 |
st.markdown("---")
|
| 740 |
|
| 741 |
-
#
|
| 742 |
-
if not g.get(
|
| 743 |
st.info(f"Share this Game ID: {gid}")
|
| 744 |
render_copy_button(gid, gid)
|
| 745 |
|
| 746 |
-
if st.session_state.get(
|
| 747 |
if st.button(f"Invite your friends to {gid}", key=f"invite_{gid}"):
|
| 748 |
-
friends = get_friends_map().get(st.session_state[
|
| 749 |
if not friends:
|
| 750 |
st.warning("No friends to invite.")
|
| 751 |
else:
|
| 752 |
for f in friends:
|
| 753 |
-
send_game_invite(st.session_state[
|
| 754 |
st.success("Invites sent to friends.")
|
| 755 |
|
| 756 |
if st.button(f"Challenge friends with a new game like {gid}", key=f"challenge_{gid}"):
|
| 757 |
new_id = create_game(
|
| 758 |
-
st.session_state.get(
|
| 759 |
-
g.get(
|
| 760 |
-
num_questions=len(g.get(
|
| 761 |
)
|
| 762 |
-
|
| 763 |
-
st.session_state["current_game_id"] = new_id
|
| 764 |
st.session_state["game_questions"] = games.get(new_id, {}).get("questions", [])
|
| 765 |
st.success(f"Challenge created: {new_id}")
|
| 766 |
-
st.
|
|
|
|
|
|
|
| 767 |
|
| 768 |
# Weekly leaderboard
|
| 769 |
st.subheader("๐ Weekly Leaderboard (Top 10)")
|
|
@@ -1117,7 +1178,9 @@ def inbox_page():
|
|
| 1117 |
def leaderboard_page():
|
| 1118 |
st.header("Leaderboard")
|
| 1119 |
|
| 1120 |
-
|
|
|
|
|
|
|
| 1121 |
if isinstance(rows, list) and rows:
|
| 1122 |
df = pd.DataFrame(rows)
|
| 1123 |
else:
|
|
@@ -1130,9 +1193,14 @@ def leaderboard_page():
|
|
| 1130 |
st.info("No scores yet.")
|
| 1131 |
return
|
| 1132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
# ๐ Stable sorting
|
| 1134 |
sort_cols = ["score"]
|
| 1135 |
asc = [False]
|
|
|
|
| 1136 |
if "timestamp" in df.columns:
|
| 1137 |
sort_cols.append("timestamp")
|
| 1138 |
asc.append(True)
|
|
@@ -1146,10 +1214,10 @@ def leaderboard_page():
|
|
| 1146 |
st.markdown(f"**๐ฅ Total Players:** {total_players}")
|
| 1147 |
st.markdown(f"**๐ฎ Total Games:** {total_games}")
|
| 1148 |
|
| 1149 |
-
# ๐ฅ TOP 3 PODIUM
|
| 1150 |
st.markdown("### ๐ Top 3 Overall")
|
| 1151 |
top3 = df_sorted.head(3)
|
| 1152 |
-
for
|
| 1153 |
st.write(
|
| 1154 |
f"{row.get('avatar','๐ฎ')} **{row.get('name','Unknown')}** โ "
|
| 1155 |
f"{row.get('score',0)} pts"
|
|
@@ -1163,25 +1231,33 @@ def leaderboard_page():
|
|
| 1163 |
# Existing chart (unchanged)
|
| 1164 |
fig = px.bar(
|
| 1165 |
df_sorted.head(10),
|
| 1166 |
-
x=
|
| 1167 |
-
y=
|
| 1168 |
)
|
| 1169 |
st.plotly_chart(fig, use_container_width=True)
|
| 1170 |
|
| 1171 |
|
| 1172 |
-
|
| 1173 |
-
|
|
|
|
| 1174 |
one_week_ago = datetime.utcnow() - timedelta(days=7)
|
| 1175 |
|
| 1176 |
weekly = []
|
| 1177 |
for r in rows:
|
| 1178 |
-
ts =
|
| 1179 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1180 |
weekly.append(r)
|
| 1181 |
|
| 1182 |
-
# Sort by score
|
| 1183 |
weekly.sort(key=lambda x: x.get("score", 0), reverse=True)
|
| 1184 |
-
return weekly[:
|
| 1185 |
# Route pages
|
| 1186 |
if page == "Home":
|
| 1187 |
home_page()
|
|
|
|
| 415 |
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
|
| 416 |
df.to_csv(LEADERBOARD_FILE, index=False)
|
| 417 |
|
| 418 |
+
|
| 419 |
+
def get_weekly_leaderboard(limit=10):
|
| 420 |
+
mode = st.session_state.get("mode_selection", "Offline")
|
| 421 |
+
rows = []
|
| 422 |
+
|
| 423 |
+
if mode == "Online":
|
| 424 |
+
ok, _ = init_firebase_if_needed()
|
| 425 |
+
if ok:
|
| 426 |
+
rows = fb_get("/leaderboard") or []
|
| 427 |
+
else:
|
| 428 |
+
try:
|
| 429 |
+
rows = pd.read_csv(LEADERBOARD_FILE).to_dict("records")
|
| 430 |
+
except Exception:
|
| 431 |
+
rows = []
|
| 432 |
+
|
| 433 |
+
if not rows:
|
| 434 |
+
return []
|
| 435 |
+
|
| 436 |
+
# Start of current week (Monday)
|
| 437 |
+
now = datetime.utcnow()
|
| 438 |
+
start_of_week = now - timedelta(days=now.weekday())
|
| 439 |
+
|
| 440 |
+
weekly = []
|
| 441 |
+
for r in rows:
|
| 442 |
+
ts = r.get("timestamp")
|
| 443 |
+
if not ts:
|
| 444 |
+
continue
|
| 445 |
+
try:
|
| 446 |
+
played = datetime.fromisoformat(ts)
|
| 447 |
+
except Exception:
|
| 448 |
+
continue
|
| 449 |
+
|
| 450 |
+
if played >= start_of_week:
|
| 451 |
+
weekly.append(r)
|
| 452 |
+
|
| 453 |
+
weekly.sort(key=lambda x: x.get("score", 0), reverse=True)
|
| 454 |
+
return weekly[:limit]
|
| 455 |
+
|
| 456 |
+
|
| 457 |
# ----------------- Session & Presence helpers -----------------
|
| 458 |
def now_iso():
|
| 459 |
return datetime.utcnow().isoformat()
|
|
|
|
| 567 |
|
| 568 |
return df.head(3).to_dict(orient="records")
|
| 569 |
|
| 570 |
+
#------Total players--------------------
|
| 571 |
+
def get_total_online_players():
|
| 572 |
+
players = unified_get("players") or {}
|
| 573 |
+
total = 0
|
| 574 |
+
for gid in players:
|
| 575 |
+
total += len(players[gid])
|
| 576 |
+
return total
|
| 577 |
+
|
| 578 |
# ----------------- Create Game Page -----------------
|
| 579 |
+
|
| 580 |
def create_game_page():
|
| 581 |
st.header("Create Game")
|
| 582 |
|
|
|
|
| 743 |
st.header("Home")
|
| 744 |
st.write("Create games, invite friends, play and climb the leaderboard.")
|
| 745 |
|
| 746 |
+
# ---------------- TOTAL ONLINE PLAYERS ----------------
|
| 747 |
+
players_map = unified_get("players") or {}
|
| 748 |
+
total_online = sum(len(v) for v in players_map.values())
|
| 749 |
+
st.metric("๐ข Players Online", total_online)
|
| 750 |
+
|
| 751 |
# Show last score
|
| 752 |
if st.session_state.get('last_score') is not None:
|
| 753 |
+
st.success(
|
| 754 |
+
f"Your last score: {st.session_state['last_score']} "
|
| 755 |
+
f"(Game {st.session_state.get('last_game')})"
|
| 756 |
+
)
|
| 757 |
|
| 758 |
+
# Load games
|
| 759 |
games = unified_get("games") or {}
|
|
|
|
| 760 |
|
| 761 |
st.subheader("Recent games")
|
| 762 |
+
|
| 763 |
for g in sorted(games.values(), key=lambda x: x.get("created_at", ""), reverse=True)[:10]:
|
| 764 |
gid = g.get("game_id")
|
| 765 |
+
players_here = players_map.get(gid, {}) or {}
|
| 766 |
+
|
| 767 |
st.markdown(f"### ๐ฎ Game: **{gid}** {'(Closed)' if g.get('closed') else ''}")
|
| 768 |
st.write(f"Host: {g.get('host')} โ Topics: {', '.join(g.get('topics', []))}")
|
| 769 |
st.write(f"Created: {g.get('created_at')}")
|
| 770 |
|
| 771 |
+
# ---------------- GAME STATS ----------------
|
| 772 |
+
joined = len(players_here)
|
| 773 |
+
submitted = sum(1 for p in players_here.values() if p.get("submitted"))
|
| 774 |
+
|
| 775 |
+
st.write(f"Players joined: **{joined}**")
|
| 776 |
+
st.write(f"Submitted: **{submitted} / {joined}**")
|
| 777 |
+
|
| 778 |
+
# ---------------- TOP 3 PLAYERS ----------------
|
| 779 |
+
submitted_players = [
|
| 780 |
+
(u, d) for u, d in players_here.items() if d.get("submitted")
|
| 781 |
+
]
|
| 782 |
+
submitted_players.sort(key=lambda x: x[1].get("score", 0), reverse=True)
|
| 783 |
+
|
| 784 |
+
if submitted_players:
|
| 785 |
+
st.markdown("๐ **Top 3 Players**")
|
| 786 |
+
for i, (uname_p, info) in enumerate(submitted_players[:3], start=1):
|
| 787 |
+
st.write(
|
| 788 |
+
f"{i}. {info.get('avatar','๐ฎ')} **{uname_p}** โ "
|
| 789 |
+
f"{info.get('score',0)} pts ({info.get('percentage',0)}%)"
|
| 790 |
+
)
|
| 791 |
|
| 792 |
+
# ---------------- PLAYER STATUS ----------------
|
| 793 |
if players_here:
|
| 794 |
st.markdown("**๐ฅ Player Status**")
|
| 795 |
for uname_p, info in players_here.items():
|
| 796 |
+
status = "โ
Submitted" if info.get("submitted") else "โณ Playing"
|
| 797 |
st.write(f"{info.get('avatar','๐ฎ')} **{uname_p}** โ {status}")
|
| 798 |
|
| 799 |
st.markdown("---")
|
| 800 |
|
| 801 |
+
# ---------------- INVITE & CHALLENGE ----------------
|
| 802 |
+
if not g.get("closed"):
|
| 803 |
st.info(f"Share this Game ID: {gid}")
|
| 804 |
render_copy_button(gid, gid)
|
| 805 |
|
| 806 |
+
if st.session_state.get("username"):
|
| 807 |
if st.button(f"Invite your friends to {gid}", key=f"invite_{gid}"):
|
| 808 |
+
friends = get_friends_map().get(st.session_state["username"], [])
|
| 809 |
if not friends:
|
| 810 |
st.warning("No friends to invite.")
|
| 811 |
else:
|
| 812 |
for f in friends:
|
| 813 |
+
send_game_invite(st.session_state["username"], f, gid)
|
| 814 |
st.success("Invites sent to friends.")
|
| 815 |
|
| 816 |
if st.button(f"Challenge friends with a new game like {gid}", key=f"challenge_{gid}"):
|
| 817 |
new_id = create_game(
|
| 818 |
+
st.session_state.get("username", "Host"),
|
| 819 |
+
g.get("topics", []),
|
| 820 |
+
num_questions=len(g.get("questions", []))
|
| 821 |
)
|
| 822 |
+
st.session_state["active_game_id"] = new_id
|
|
|
|
| 823 |
st.session_state["game_questions"] = games.get(new_id, {}).get("questions", [])
|
| 824 |
st.success(f"Challenge created: {new_id}")
|
| 825 |
+
st.rerun()
|
| 826 |
+
|
| 827 |
+
|
| 828 |
|
| 829 |
# Weekly leaderboard
|
| 830 |
st.subheader("๐ Weekly Leaderboard (Top 10)")
|
|
|
|
| 1178 |
def leaderboard_page():
|
| 1179 |
st.header("Leaderboard")
|
| 1180 |
|
| 1181 |
+
# โ
Correct source (Online + Offline safe)
|
| 1182 |
+
rows = unified_get("/leaderboard") or []
|
| 1183 |
+
|
| 1184 |
if isinstance(rows, list) and rows:
|
| 1185 |
df = pd.DataFrame(rows)
|
| 1186 |
else:
|
|
|
|
| 1193 |
st.info("No scores yet.")
|
| 1194 |
return
|
| 1195 |
|
| 1196 |
+
# โ
Ensure numeric score
|
| 1197 |
+
if "score" in df.columns:
|
| 1198 |
+
df["score"] = pd.to_numeric(df["score"], errors="coerce").fillna(0)
|
| 1199 |
+
|
| 1200 |
# ๐ Stable sorting
|
| 1201 |
sort_cols = ["score"]
|
| 1202 |
asc = [False]
|
| 1203 |
+
|
| 1204 |
if "timestamp" in df.columns:
|
| 1205 |
sort_cols.append("timestamp")
|
| 1206 |
asc.append(True)
|
|
|
|
| 1214 |
st.markdown(f"**๐ฅ Total Players:** {total_players}")
|
| 1215 |
st.markdown(f"**๐ฎ Total Games:** {total_games}")
|
| 1216 |
|
| 1217 |
+
# ๐ฅ TOP 3 PODIUM (overall)
|
| 1218 |
st.markdown("### ๐ Top 3 Overall")
|
| 1219 |
top3 = df_sorted.head(3)
|
| 1220 |
+
for _, row in top3.iterrows():
|
| 1221 |
st.write(
|
| 1222 |
f"{row.get('avatar','๐ฎ')} **{row.get('name','Unknown')}** โ "
|
| 1223 |
f"{row.get('score',0)} pts"
|
|
|
|
| 1231 |
# Existing chart (unchanged)
|
| 1232 |
fig = px.bar(
|
| 1233 |
df_sorted.head(10),
|
| 1234 |
+
x="name",
|
| 1235 |
+
y="score"
|
| 1236 |
)
|
| 1237 |
st.plotly_chart(fig, use_container_width=True)
|
| 1238 |
|
| 1239 |
|
| 1240 |
+
|
| 1241 |
+
def get_weekly_leaderboard(limit=10):
|
| 1242 |
+
rows = unified_get("/leaderboard") or []
|
| 1243 |
one_week_ago = datetime.utcnow() - timedelta(days=7)
|
| 1244 |
|
| 1245 |
weekly = []
|
| 1246 |
for r in rows:
|
| 1247 |
+
ts = r.get("timestamp")
|
| 1248 |
+
if not ts:
|
| 1249 |
+
continue
|
| 1250 |
+
try:
|
| 1251 |
+
played_time = datetime.fromisoformat(ts)
|
| 1252 |
+
except Exception:
|
| 1253 |
+
continue
|
| 1254 |
+
|
| 1255 |
+
if played_time >= one_week_ago:
|
| 1256 |
weekly.append(r)
|
| 1257 |
|
| 1258 |
+
# Sort by score DESC
|
| 1259 |
weekly.sort(key=lambda x: x.get("score", 0), reverse=True)
|
| 1260 |
+
return weekly[:limit]
|
| 1261 |
# Route pages
|
| 1262 |
if page == "Home":
|
| 1263 |
home_page()
|