feat(backend): add service layer
This commit is contained in:
95
backend/src/services/question_service.py
Normal file
95
backend/src/services/question_service.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from uuid import UUID
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from src.models import Category, Difficulty, Pattern, Question
|
||||
from src.models.question import QuestionPattern
|
||||
|
||||
|
||||
class QuestionService:
|
||||
def __init__(self, db: AsyncSession) -> None:
|
||||
self.db = db
|
||||
|
||||
async def get_questions(
|
||||
self,
|
||||
*,
|
||||
page: int = 1,
|
||||
limit: int = 20,
|
||||
difficulties: list[Difficulty] | None = None,
|
||||
category_slug: str | None = None,
|
||||
pattern_slug: str | None = None,
|
||||
search: str | None = None,
|
||||
) -> tuple[list[Question], int]:
|
||||
query = (
|
||||
select(Question)
|
||||
.options(
|
||||
selectinload(Question.categories),
|
||||
selectinload(Question.patterns),
|
||||
selectinload(Question.question_patterns).selectinload(QuestionPattern.pattern),
|
||||
)
|
||||
.order_by(Question.created_at.desc())
|
||||
)
|
||||
|
||||
count_query = select(func.count(Question.id))
|
||||
|
||||
if difficulties:
|
||||
query = query.where(Question.difficulty.in_(difficulties))
|
||||
count_query = count_query.where(Question.difficulty.in_(difficulties))
|
||||
|
||||
if category_slug:
|
||||
query = query.join(Question.categories).where(Category.slug == category_slug)
|
||||
count_query = count_query.join(Question.categories).where(
|
||||
Category.slug == category_slug
|
||||
)
|
||||
|
||||
if pattern_slug:
|
||||
query = query.join(Question.patterns).where(Pattern.slug == pattern_slug)
|
||||
count_query = count_query.join(Question.patterns).where(Pattern.slug == pattern_slug)
|
||||
|
||||
if search:
|
||||
search_filter = Question.title.ilike(f"%{search}%")
|
||||
query = query.where(search_filter)
|
||||
count_query = count_query.where(search_filter)
|
||||
|
||||
total_result = await self.db.execute(count_query)
|
||||
total = total_result.scalar() or 0
|
||||
|
||||
offset = (page - 1) * limit
|
||||
query = query.offset(offset).limit(limit)
|
||||
|
||||
result = await self.db.execute(query)
|
||||
questions = list(result.scalars().unique().all())
|
||||
|
||||
return questions, total
|
||||
|
||||
async def get_question_by_slug(self, slug: str) -> Question | None:
|
||||
query = (
|
||||
select(Question)
|
||||
.options(
|
||||
selectinload(Question.categories),
|
||||
selectinload(Question.patterns),
|
||||
selectinload(Question.question_patterns).selectinload(QuestionPattern.pattern),
|
||||
selectinload(Question.explanation),
|
||||
selectinload(Question.solutions),
|
||||
)
|
||||
.where(Question.slug == slug)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def get_question_by_id(self, question_id: UUID) -> Question | None:
|
||||
query = (
|
||||
select(Question)
|
||||
.options(
|
||||
selectinload(Question.categories),
|
||||
selectinload(Question.patterns),
|
||||
selectinload(Question.question_patterns).selectinload(QuestionPattern.pattern),
|
||||
selectinload(Question.explanation),
|
||||
selectinload(Question.solutions),
|
||||
)
|
||||
.where(Question.id == question_id)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
return result.scalar_one_or_none()
|
||||
Reference in New Issue
Block a user