Automated Action 1754fec627 Create Bible Quiz App API with FastAPI and SQLite
- Set up project structure with FastAPI and SQLite
- Create models for users, questions, and quizzes
- Implement Alembic migrations with seed data
- Add user authentication with JWT
- Implement question management endpoints
- Implement quiz creation and management
- Add quiz-taking and scoring functionality
- Set up API documentation and health check endpoint
- Update README with comprehensive documentation
2025-06-03 15:46:44 +00:00

179 lines
5.2 KiB
Python

from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.api.deps import get_current_active_admin, get_current_active_user
from app.db.session import get_db
from app.models.user import User
from app.schemas.bible_book import BibleBook, Testament
from app.schemas.question import (
Question,
QuestionCategory,
QuestionCreate,
QuestionDifficulty,
QuestionUpdate,
)
from app.services import question as question_service
router = APIRouter()
@router.get("/", response_model=List[Question])
def read_questions(
db: Session = Depends(get_db),
skip: int = 0,
limit: int = 100,
category_id: Optional[int] = None,
difficulty_id: Optional[int] = None,
bible_book_id: Optional[int] = None,
current_user: User = Depends(get_current_active_user),
) -> Any:
"""
Retrieve questions with optional filtering.
"""
questions = question_service.get_multi(
db,
skip=skip,
limit=limit,
category_id=category_id,
difficulty_id=difficulty_id,
bible_book_id=bible_book_id,
)
return questions
@router.post("/", response_model=Question)
def create_question(
*,
db: Session = Depends(get_db),
question_in: QuestionCreate,
current_user: User = Depends(get_current_active_admin),
) -> Any:
"""
Create new question with options. Admin only.
"""
# Validate that at least one option is marked as correct
if not any(option.is_correct for option in question_in.options):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="At least one option must be marked as correct",
)
question = question_service.create(db, obj_in=question_in)
return question
@router.get("/{question_id}", response_model=Question)
def read_question(
*,
db: Session = Depends(get_db),
question_id: int,
current_user: User = Depends(get_current_active_user),
) -> Any:
"""
Get question by ID.
"""
question = question_service.get_by_id(db, question_id=question_id)
if not question:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Question not found",
)
return question
@router.put("/{question_id}", response_model=Question)
def update_question(
*,
db: Session = Depends(get_db),
question_id: int,
question_in: QuestionUpdate,
current_user: User = Depends(get_current_active_admin),
) -> Any:
"""
Update a question. Admin only.
"""
question = question_service.get_by_id(db, question_id=question_id)
if not question:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Question not found",
)
# If options are being updated, validate that at least one is correct
if question_in.options:
# Identify which options have is_correct field set
options_with_is_correct = [
opt for opt in question_in.options
if opt.is_correct is not None
]
# If any options have is_correct field set, at least one must be True
if options_with_is_correct and not any(opt.is_correct for opt in options_with_is_correct):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="At least one option must be marked as correct",
)
question = question_service.update(db, db_obj=question, obj_in=question_in)
return question
@router.delete("/{question_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def delete_question(
*,
db: Session = Depends(get_db),
question_id: int,
current_user: User = Depends(get_current_active_admin),
) -> Any:
"""
Delete a question. Admin only.
"""
question = question_service.get_by_id(db, question_id=question_id)
if not question:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Question not found",
)
question_service.remove(db, question_id=question_id)
return None
# Endpoints for categories, difficulties, and Bible books
@router.get("/categories/", response_model=List[QuestionCategory])
def read_categories(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user),
) -> Any:
"""
Retrieve all question categories.
"""
return question_service.get_all_categories(db)
@router.get("/difficulties/", response_model=List[QuestionDifficulty])
def read_difficulties(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user),
) -> Any:
"""
Retrieve all question difficulties.
"""
return question_service.get_all_difficulties(db)
@router.get("/bible-books/", response_model=List[BibleBook])
def read_bible_books(
db: Session = Depends(get_db),
testament: Optional[Testament] = None,
current_user: User = Depends(get_current_active_user),
) -> Any:
"""
Retrieve all Bible books, optionally filtered by testament.
"""
if testament:
return question_service.get_bible_books_by_testament(db, testament=testament.value)
return question_service.get_all_bible_books(db)