UI/UX Polishing: CBT dashboard, sidebar tiers, landing pricing, and exam disclaimer

This commit is contained in:
2026-07-09 17:05:40 +05:30
parent b80d73ec26
commit 22b167b985
15 changed files with 481 additions and 202 deletions

View File

@@ -85,6 +85,40 @@ def get_test_by_id(test_id: UUID, user_id: UUID, session: Session = Depends(get_
raise HTTPException(status_code=404, detail="Requested examination structure not found.")
return test
@app.get("/api/tests/{test_id}/questions", response_model=List[models.Question], tags=["CBT Engine"])
def get_questions_by_test_id(test_id: UUID, session: Session = Depends(get_session)):
test = session.get(models.Test, test_id)
if not test:
raise HTTPException(status_code=404, detail="Requested examination structure not found.")
# Extract all question IDs from sections
question_ids = []
for section_name, section_data in test.sections.items():
question_ids.extend(section_data.get("questions", []))
# Fetch all questions in one query
if not question_ids:
return []
# Ensure they are UUID objects
import uuid
valid_ids = []
for q_id in question_ids:
if isinstance(q_id, str):
try:
valid_ids.append(uuid.UUID(q_id))
except ValueError:
continue
elif isinstance(q_id, uuid.UUID):
valid_ids.append(q_id)
query = select(models.Question).where(models.Question.id.in_(valid_ids))
questions = session.exec(query).all()
# Order them to match the section order if needed, but the frontend currently doesn't rely on it strictly
# except for section bounds which it reconstructs.
return questions
@app.get("/api/questions/{question_id}", response_model=models.Question, tags=["CBT Engine"])
def get_question_by_id(question_id: UUID, session: Session = Depends(get_session)):
question = session.get(models.Question, question_id)
@@ -143,7 +177,7 @@ def submit_test_attempt(payload: Dict[str, Any], session: Session = Depends(get_
if not question:
continue
correct_ans = question.correct_answer
correct_ans = question.correct_answer or {}
# Support both {"value": "A"} and {"correct": "A"} key formats
correct_value = correct_ans.get("value") or correct_ans.get("correct")
@@ -151,8 +185,13 @@ def submit_test_attempt(payload: Dict[str, Any], session: Session = Depends(get_
# Subjective: award credit for any non-trivial written answer
student_text = str(selected or "").strip()
is_correct = len(student_text) > 5
elif isinstance(correct_value, list):
# Multiple Choice (Multi-Correct)
selected_list = sorted([s.strip() for s in str(selected or "").split(",") if s.strip()])
correct_list = sorted([str(c).strip() for c in correct_value])
is_correct = (selected_list == correct_list)
else:
is_correct = bool(correct_value) and correct_value == selected
is_correct = bool(correct_value) and str(correct_value).strip() == str(selected or "").strip()
if is_correct:
correct_count += 1
@@ -241,8 +280,16 @@ def submit_mistake_review(payload: Dict[str, Any], session: Session = Depends(ge
raise HTTPException(status_code=404, detail="Mistake not found.")
question = session.get(models.Question, mistake.question_id)
correct_value = question.correct_answer.get("value") or question.correct_answer.get("correct")
is_correct = correct_value == selected_answer
correct_ans = question.correct_answer or {}
correct_value = correct_ans.get("value") or correct_ans.get("correct")
if isinstance(correct_value, list):
# Multiple Choice (Multi-Correct)
selected_list = sorted([s.strip() for s in str(selected_answer or "").split(",") if s.strip()])
correct_list = sorted([str(c).strip() for c in correct_value])
is_correct = (selected_list == correct_list)
else:
is_correct = bool(correct_value) and str(correct_value).strip() == str(selected_answer or "").strip()
q_value = 4 if is_correct else 1
days_since_creation = (datetime.utcnow() - mistake.created_at).days
@@ -450,6 +497,21 @@ def update_user_profile(user_id: UUID, payload: UserUpdate, session: Session = D
session.refresh(user)
return user
class UserSessionUpdate(BaseModel):
session_token: str
@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)
if not user:
raise HTTPException(status_code=404, detail="User not found")
user.active_session_token = payload.session_token
session.add(user)
session.commit()
session.refresh(user)
return user
from storage import upload_file
@app.post("/api/users/{user_id}/upload-avatar", tags=["Users"])