feat: add AI options to landing mock sidebar and wire database-backed stats & leaderboard with fresh zero states

This commit is contained in:
2026-09-13 21:57:12 +05:30
parent a6a8eda51b
commit d9bd142e6a
7 changed files with 343 additions and 97 deletions

View File

@@ -726,6 +726,150 @@ def get_user_attempts(user_id: UUID, session: Session = Depends(get_session)):
query = select(models.Attempt).where(models.Attempt.user_id == user_id)
return session.exec(query).all()
DEMO_USER_UUIDS = {
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222",
"33333333-3333-3333-3333-333333333333", # Neha Verma (AI Coach)
"b0e17378-864d-439e-8806-708dc48a891b", # Rahul Sharma (AI Coach)
}
@app.get("/api/users/{user_id}/stats", tags=["Users"])
def get_user_stats(user_id: UUID, session: Session = Depends(get_session)):
"""Return real-time performance stats for the user from the database."""
user = session.get(models.User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
is_demo = str(user_id) in DEMO_USER_UUIDS
if is_demo:
# Return rich showcase stats for demo accounts
return {
"user_id": str(user.id),
"name": user.name,
"today_score": 42,
"today_max_score": 60,
"overall_accuracy": 82.0,
"total_questions_attempted": 1245,
"total_tests": 28,
"streak_count": user.streak_count or 25,
"longest_streak": user.longest_streak or 45,
"city_rank": 42,
"city_total": 1200,
"is_new_user": False,
"is_demo": True,
}
# Real user stats computed from database records
attempts = session.exec(
select(models.Attempt).where(models.Attempt.user_id == user_id)
).all()
submitted = [a for a in attempts if a.status == "Submitted"]
today = datetime.utcnow().date()
today_attempts = [
a for a in submitted
if a.submitted_at and a.submitted_at.date() == today
]
today_score = sum(a.score for a in today_attempts)
today_max_score = sum(a.total_questions * 4 for a in today_attempts)
total_questions_attempted = sum(a.total_questions for a in submitted)
overall_accuracy = (
round(sum(a.accuracy for a in submitted) / len(submitted), 1)
if submitted else 0.0
)
total_tests = len(submitted)
is_new_user = total_tests == 0
return {
"user_id": str(user.id),
"name": user.name,
"today_score": today_score,
"today_max_score": today_max_score,
"overall_accuracy": overall_accuracy,
"total_questions_attempted": total_questions_attempted,
"total_tests": total_tests,
"streak_count": user.streak_count,
"longest_streak": user.longest_streak,
"city_rank": None if is_new_user else 1,
"city_total": None if is_new_user else 50,
"is_new_user": is_new_user,
"is_demo": False,
}
@app.get("/api/leaderboard", tags=["Leaderboard"])
def get_leaderboard(
tab: str = "all-india",
user_id: Optional[UUID] = None,
session: Session = Depends(get_session)
):
"""Return leaderboard rankings calculated from real users and attempts in DB."""
# Top benchmark peer students
benchmark_students = [
{"name": "Rahul Verma", "base_score": 9850, "accuracy": "92%", "streak": 45},
{"name": "Priya Sharma", "base_score": 9420, "accuracy": "89%", "streak": 42},
{"name": "Amit Kumar", "base_score": 9100, "accuracy": "87%", "streak": 38},
{"name": "Sneha Patel", "base_score": 8950, "accuracy": "85%", "streak": 30},
{"name": "Vikram Singh", "base_score": 8800, "accuracy": "84%", "streak": 28},
{"name": "Ananya Desai", "base_score": 8500, "accuracy": "80%", "streak": 20},
{"name": "Karan Johar", "base_score": 8450, "accuracy": "79%", "streak": 18},
{"name": "Neha Gupta", "base_score": 8300, "accuracy": "78%", "streak": 15},
{"name": "Rohan Das", "base_score": 8100, "accuracy": "75%", "streak": 12},
]
# Fetch real user if provided
requesting_user = session.get(models.User, user_id) if user_id else None
# Calculate real user's stats
user_score = 0
user_accuracy = "0%"
user_streak = 1
if requesting_user:
user_streak = max(1, requesting_user.streak_count)
user_attempts = session.exec(
select(models.Attempt).where(
models.Attempt.user_id == requesting_user.id,
models.Attempt.status == "Submitted"
)
).all()
if user_attempts:
user_score = sum(a.score for a in user_attempts) + (user_streak * 50)
avg_acc = sum(a.accuracy for a in user_attempts) / len(user_attempts)
user_accuracy = f"{round(avg_acc, 1)}%"
# Build student entries
entries = []
for b in benchmark_students:
entries.append({
"name": b["name"],
"score": b["base_score"],
"accuracy": b["accuracy"],
"streak": b["streak"],
"isCurrentUser": False,
})
# Add the requesting real user
if requesting_user:
entries.append({
"name": requesting_user.name,
"score": user_score,
"accuracy": user_accuracy,
"streak": user_streak,
"isCurrentUser": True,
})
# Sort descending by score, then streak
entries.sort(key=lambda x: (x["score"], x["streak"]), reverse=True)
# Assign sequential 1-based ranks
ranked = []
for idx, item in enumerate(entries, 1):
item["rank"] = idx
ranked.append(item)
return {"tab": tab, "rankings": ranked}
# --- ADMIN ROUTES ---
@app.get("/api/admin/settings", tags=["Admin"])