fix: QA report BUG-01 to BUG-12 full resolution - demo switcher, AI tutor stream, timeline alignment, cbt filtering, danger styling, streak celebration

This commit is contained in:
2026-09-19 22:53:58 +05:30
parent 45f2f82eaf
commit 5c6ddcaca0
9 changed files with 459 additions and 273 deletions

View File

@@ -641,11 +641,110 @@ def get_referral_dashboard(user_id: UUID, session: Session = Depends(get_session
"share_message": f"I'm on a {user.streak_count}-day study streak on PragatiVeer! Use my link to get 7 days of the AI Coach for free: {link}"
}
# --- USERS ---
# --- USERS & DEMO PROFILES ---
DEMO_DEFINITIONS = {
UUID("b0e17378-864d-439e-8806-708dc48a891b"): {
"name": "Rahul Sharma",
"email": "rahul.demo@pragativeer.com",
"phone": "9999999999",
"student_class": models.ClassEnum.TWELFTH,
"medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.JEE_MAIN,
"city": "New Delhi",
"state": "Delhi",
"school": "Delhi Public School, R.K. Puram",
"tier": models.TierEnum.AI_COACH,
"streak_count": 6,
"longest_streak": 6,
},
UUID("33333333-3333-3333-3333-333333333333"): {
"name": "Neha Verma",
"email": "demo@pragativeer.com",
"phone": "9999999997",
"student_class": models.ClassEnum.TWELFTH,
"medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.JEE_MAIN,
"city": "New Delhi",
"state": "Delhi",
"school": "Modern School, Barakhamba Road",
"tier": models.TierEnum.AI_COACH,
"streak_count": 15,
"longest_streak": 25,
},
UUID("22222222-2222-2222-2222-222222222222"): {
"name": "Priya Singh",
"email": "priya.demo@pragativeer.com",
"phone": "9999999996",
"student_class": models.ClassEnum.TWELFTH,
"medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.NEET,
"city": "Lucknow",
"state": "Uttar Pradesh",
"school": "City Montessori School",
"tier": models.TierEnum.PRO,
"streak_count": 42,
"longest_streak": 42,
},
UUID("11111111-1111-1111-1111-111111111111"): {
"name": "Amit Kumar",
"email": "amit.demo@pragativeer.com",
"phone": "9999999995",
"student_class": models.ClassEnum.ELEVENTH,
"medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.JEE_MAIN,
"city": "Patna",
"state": "Bihar",
"school": "Loyola High School",
"tier": models.TierEnum.FREE,
"streak_count": 38,
"longest_streak": 38,
},
}
def ensure_demo_user(user_id: UUID, session: Session) -> Optional[models.User]:
"""Ensures a requested demo UUID exists in the DB so demo switching never 404s (BUG-03)."""
user = session.get(models.User, user_id)
if user:
return user
if user_id in DEMO_DEFINITIONS:
data = DEMO_DEFINITIONS[user_id]
import bcrypt
new_demo = models.User(
id=user_id,
password_hash=bcrypt.hashpw("Pv9#kL2$mQ8!vX4@".encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
last_active=datetime.utcnow(),
**data
)
session.add(new_demo)
session.commit()
session.refresh(new_demo)
return new_demo
return None
@app.get("/api/user/switch", tags=["Users"])
@app.post("/api/user/switch", tags=["Users"])
def switch_user_endpoint(id: str, session: Session = Depends(get_session)):
"""BUG-03 fix: Dedicated switch user endpoint that supports legacy aliases (usr_02 etc.) and UUIDs."""
mapping = {
"usr_01": "11111111-1111-1111-1111-111111111111",
"usr_02": "22222222-2222-2222-2222-222222222222",
"usr_03": "33333333-3333-3333-3333-333333333333",
"usr_04": "b0e17378-864d-439e-8806-708dc48a891b",
}
target_id_str = mapping.get(id, id)
try:
uid = UUID(target_id_str)
except Exception:
raise HTTPException(status_code=400, detail="Invalid user ID format")
user = ensure_demo_user(uid, session) or session.get(models.User, uid)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return {"status": "success", "user_id": str(user.id), "name": user.name}
@app.get("/api/users/{user_id}", response_model=models.User, tags=["Users"])
def get_user_profile(user_id: UUID, session: Session = Depends(get_session)):
user = session.get(models.User, user_id)
user = ensure_demo_user(user_id, session) or session.get(models.User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
@@ -707,7 +806,7 @@ class UserSessionUpdate(BaseModel):
@app.post("/api/users/{user_id}/session", response_model=models.User, tags=["Users"])
def update_user_session(user_id: UUID, payload: UserSessionUpdate, session: Session = Depends(get_session)):
user = session.get(models.User, user_id)
user = ensure_demo_user(user_id, session) or session.get(models.User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
@@ -751,7 +850,7 @@ DEMO_USER_UUIDS = {
@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)
user = ensure_demo_user(user_id, session) or session.get(models.User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
@@ -833,20 +932,25 @@ def get_leaderboard(
{"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
# Fetch real user if provided (with demo auto-provision fallback)
requesting_user = ensure_demo_user(user_id, session) if user_id else None
if not requesting_user and user_id:
requesting_user = session.get(models.User, user_id)
# Calculate real user's stats
user_score = 0
user_accuracy = "0%"
user_streak = 0 # BUG-04 fix: real zero for new users (no artificial min floor)
user_streak = 0
if requesting_user:
if str(requesting_user.id) in DEMO_USER_UUIDS:
user_score = 9250
user_accuracy = "88.5%"
user_streak = requesting_user.streak_count or 25
# BUG-07 fix: Harmonize demo score with Accuracy x Streak Bonus formula
user_streak = requesting_user.streak_count if requesting_user.streak_count is not None else 15
acc_val = 88.5
user_accuracy = f"{acc_val}%"
# Score formula: base accuracy + streak consistency bonus
user_score = int((acc_val * 90) + (user_streak * 35))
else:
user_streak = requesting_user.streak_count or 0 # BUG-04 fix: 0 is valid
user_streak = requesting_user.streak_count or 0
user_attempts = session.exec(
select(models.Attempt).where(
models.Attempt.user_id == requesting_user.id,