diff --git a/app/crud/__init__.py b/app/crud/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/crud/words.py b/app/crud/words.py new file mode 100644 index 0000000..593437b --- /dev/null +++ b/app/crud/words.py @@ -0,0 +1,53 @@ +import logging + +from sqlalchemy import or_, select +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models.words import Word +from app.schemas.words import WordCreateSchema + +logger = logging.getLogger(__name__) + + +class CRUDWord: + + async def create_word(self, word: WordCreateSchema, session: AsyncSession): + try: + new_word = Word(**word.model_dump()) + session.add(new_word) + await session.flush() + await session.commit() + await session.refresh(new_word) + logger.info(f"Добавлеено слово {new_word}") + return new_word + except SQLAlchemyError as e: + logger.error(f"Ошибка при добавлении слова: {e}") + raise + + async def get_all_words(self, session: AsyncSession): + try: + query = select(Word) + result = await session.execute(query) + words = result.scalars().all() + logger.info("Получен список всех слов") + return words + except SQLAlchemyError as e: + logger.error(f"Ошибка при получении списка слов: {e}") + raise + + async def get_word(self, word: str, session: AsyncSession): + try: + query = select(Word).where( + or_(Word.english == word, Word.russian == word) + ) + result = await session.execute(query) + find_word = result.scalars().first() + if find_word: + logger.info(f"Получено слово: '{find_word}'") + else: + logger.warning(f"Слово '{word}' не найдено в базе") + return find_word + except SQLAlchemyError as e: + logger.error(f"Ошибка при получении слова {word}: {e}") + raise diff --git a/app/main.py b/app/main.py index 6f38f8c..3be0b17 100644 --- a/app/main.py +++ b/app/main.py @@ -5,6 +5,8 @@ from app.core.config import settings from app.core.logging import setup_logging +from app.routers.questions import question_router +from app.routers.words import word_router setup_logging() @@ -23,3 +25,6 @@ async def lifespan(app: FastAPI): description=settings.description, lifespan=lifespan, ) + +app.include_router(word_router) +app.include_router(question_router) diff --git a/app/routers/questions.py b/app/routers/questions.py new file mode 100644 index 0000000..66b138d --- /dev/null +++ b/app/routers/questions.py @@ -0,0 +1,53 @@ +import random + +from fastapi import APIRouter, Depends +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.database import get_session +from app.crud.words import CRUDWord +from app.schemas.words import QuestionResponseSchema + +question_router = APIRouter( + prefix="/questions", + tags=[ + "Вопросы", + ], +) + +word_crud = CRUDWord() + + +@question_router.get( + "", response_model=QuestionResponseSchema, summary="Получение вопроса" +) +async def get_question( + session: AsyncSession = Depends(get_session), +) -> QuestionResponseSchema: + words = await word_crud.get_all_words(session) + if len(words) < 4: + return {"error": "В базее должно быть минимум 4 слова"} + + correct_word = random.choice(words) + options_words = random.sample(words, 4) + direction = random.choice(["en-ru", "ru-en"]) + + if direction == "en-ru": + question_word = correct_word.english + correct_answer = correct_word.russian + options = [w.russian for w in options_words] + else: + question_word = correct_word.russian + correct_answer = correct_word.english + options = [w.english for w in options_words] + + if correct_answer not in options: + options[0] = correct_answer + + random.shuffle(options) + + return { + "word": question_word, + "options": options, + "correct": correct_answer, + "direction": direction, + } diff --git a/app/routers/words.py b/app/routers/words.py new file mode 100644 index 0000000..5501d66 --- /dev/null +++ b/app/routers/words.py @@ -0,0 +1,69 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.database import get_session +from app.crud.words import CRUDWord +from app.schemas.words import WordCreateSchema, WordReadSchema + +word_router = APIRouter( + prefix="/words", + tags=[ + "Слова", + ], +) + +word_crud = CRUDWord() + + +@word_router.post( + "", + response_model=WordReadSchema, + summary="Добавление слова", +) +async def add_word( + word: WordCreateSchema, session: AsyncSession = Depends(get_session) +) -> WordReadSchema: + try: + add_word = await word_crud.create_word(word, session) + return add_word + except Exception as e: + raise e + + +@word_router.get( + "/all", + response_model=list[WordReadSchema], + summary="Получение списка всех слов", +) +async def get_all_words( + session: AsyncSession = Depends(get_session), +) -> list[WordReadSchema]: + try: + words = await word_crud.get_all_words(session) + if not words: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Список слов отсутсттвует", + ) + return words + except Exception as e: + raise e + + +@word_router.get( + "/{word}", response_model=WordReadSchema, summary="Получение слова" +) +async def get_word( + word: str, session: AsyncSession = Depends(get_session) +) -> WordReadSchema: + try: + word = word.lower() + find_word = await word_crud.get_word(word, session) + if not find_word: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Слово не найдено", + ) + return find_word + except Exception as e: + raise e diff --git a/app/schemas/words.py b/app/schemas/words.py index cf3bc31..5fde274 100644 --- a/app/schemas/words.py +++ b/app/schemas/words.py @@ -3,7 +3,7 @@ from pydantic import BaseModel -class QuestionResponse(BaseModel): +class QuestionResponseSchema(BaseModel): word: str options: list[str] correct: str