Files
pyisu/backend/app/routes/learn.py
2026-03-13 14:39:43 +08:00

180 lines
6.1 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import Optional
from .. import crud, schemas, auth
from ..database import get_db
from ..models import User
router = APIRouter(prefix="/learn", tags=["learning"])
@router.get("/steps/{step_id}", response_model=schemas.SuccessResponse)
def get_step_content(
step_id: int,
db: Session = Depends(get_db),
current_user: Optional[User] = Depends(auth.get_optional_current_user)
):
step = crud.get_step_by_id(db, step_id=step_id)
if not step:
raise HTTPException(status_code=404, detail="Step not found")
lesson = crud.get_lesson_by_id(db, step.lesson_id)
if not lesson:
raise HTTPException(status_code=404, detail="Lesson not found")
module = crud.get_module_by_id(db, lesson.module_id)
if not module:
raise HTTPException(status_code=404, detail="Module not found")
from ..models import Course
course = db.query(Course).filter(Course.id == module.course_id).first()
if not course:
raise HTTPException(status_code=404, detail="Course not found")
if course.status != 'published':
if current_user is None or course.author_id != current_user.id:
raise HTTPException(status_code=403, detail="Not authorized")
return schemas.SuccessResponse(data={
"step": {
"id": step.id,
"step_type": step.step_type,
"title": step.title,
"content": step.content,
"content_html": step.content_html,
"file_url": step.file_url,
"test_settings": step.test_settings,
"order_index": step.order_index
}
})
@router.get("/steps/{step_id}/test", response_model=schemas.SuccessResponse)
def get_test_for_viewing(
step_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(auth.get_current_user)
):
"""
Get test questions for viewing (without correct answers)
"""
step = crud.get_step_by_id(db, step_id)
if not step:
raise HTTPException(status_code=404, detail="Step not found")
if step.step_type != 'test':
raise HTTPException(status_code=400, detail="Step is not a test")
questions = crud.get_test_questions(db, step_id)
# Prepare questions without correct answers
questions_for_viewer = []
for q in questions:
question_data = {
"id": q.id,
"question_text": q.question_text,
"question_type": q.question_type,
"points": q.points,
"order_index": q.order_index
}
if q.question_type in ['single_choice', 'multiple_choice']:
# Return answers without is_correct flag
question_data["answers"] = [
{"id": a.id, "answer_text": a.answer_text, "order_index": a.order_index}
for a in q.answers
]
elif q.question_type == 'match':
# Return match items shuffled
question_data["match_items"] = [
{"id": m.id, "left_text": m.left_text, "right_text": m.right_text, "order_index": m.order_index}
for m in q.match_items
]
questions_for_viewer.append(question_data)
# Get user's previous attempts
attempts = crud.get_test_attempts(db, current_user.id, step_id)
attempts_data = [
{
"id": a.id,
"score": float(a.score),
"max_score": float(a.max_score),
"passed": a.passed,
"completed_at": a.completed_at.isoformat() if a.completed_at else None
}
for a in attempts
]
return schemas.SuccessResponse(data={
"questions": questions_for_viewer,
"settings": step.test_settings,
"attempts": attempts_data
})
@router.post("/steps/{step_id}/test/submit", response_model=schemas.SuccessResponse)
def submit_test(
step_id: int,
attempt: schemas.TestAttemptCreate,
db: Session = Depends(get_db),
current_user: User = Depends(auth.get_current_user)
):
"""
Submit test answers and get results
"""
step = crud.get_step_by_id(db, step_id)
if not step:
raise HTTPException(status_code=404, detail="Step not found")
if step.step_type != 'test':
raise HTTPException(status_code=400, detail="Step is not a test")
# Check max attempts limit
settings = step.test_settings or {}
max_attempts = settings.get('max_attempts', 0)
if max_attempts > 0:
attempts = crud.get_test_attempts(db, current_user.id, step_id)
if len(attempts) >= max_attempts:
raise HTTPException(status_code=400, detail="Maximum attempts reached")
# Create attempt and calculate score
test_attempt = crud.create_test_attempt(db, current_user.id, step_id, attempt.answers)
if not test_attempt:
raise HTTPException(status_code=500, detail="Failed to create test attempt")
return schemas.SuccessResponse(data={
"attempt": {
"id": test_attempt.id,
"score": float(test_attempt.score),
"max_score": float(test_attempt.max_score),
"passed": test_attempt.passed,
"completed_at": test_attempt.completed_at.isoformat() if test_attempt.completed_at else None
}
})
@router.get("/steps/{step_id}/test/attempts", response_model=schemas.SuccessResponse)
def get_test_attempts(
step_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(auth.get_current_user)
):
"""
Get user's test attempts history
"""
step = crud.get_step_by_id(db, step_id)
if not step:
raise HTTPException(status_code=404, detail="Step not found")
attempts = crud.get_test_attempts(db, current_user.id, step_id)
attempts_data = [
{
"id": a.id,
"score": float(a.score),
"max_score": float(a.max_score),
"passed": a.passed,
"completed_at": a.completed_at.isoformat() if a.completed_at else None
}
for a in attempts
]
return schemas.SuccessResponse(data={"attempts": attempts_data})