Skip to content

Commit

Permalink
Merge branch 'game-api-questions-v1' into game-api-v1
Browse files Browse the repository at this point in the history
  • Loading branch information
codEnjoyer committed Nov 20, 2023
2 parents 78a40e9 + a868294 commit 9eba3cc
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""переносит ответственность за правильный вариант ответа на вопроса на ответ
Revision ID: a2f543f8e4c6
Revises: f074fb69a98a
Create Date: 2023-11-20 12:48:48.808979
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'a2f543f8e4c6'
down_revision: Union[str, None] = 'f074fb69a98a'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('questions', 'correct_answer_id')
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('questions', sa.Column('correct_answer_id', sa.UUID(), autoincrement=False, nullable=False))
# ### end Alembic commands ###
6 changes: 6 additions & 0 deletions backend/src/game/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from repository.level_repository import LevelRepository
from repository.map_repository import MapRepository
from repository.module_repository import ModuleRepository
from repository.question_repository import QuestionRepository
from repository.task_unit_repository import TaskUnitRepository
from repository.theory_unit_repository import TheoryUnitRepository
from services.level_service import LevelService
from services.map_service import MapService
from services.module_service import ModuleService
from services.question_service import QuestionService
from services.task_unit_service import TaskUnitService
from services.theory_unit_service import TheoryUnitService

Expand All @@ -28,5 +30,9 @@ def task_unit_service() -> TaskUnitService:

def theory_unit_service() -> TheoryUnitService:
return TheoryUnitService(TheoryUnitRepository)


def question_service() -> QuestionService:
return QuestionService(QuestionRepository)
# def users_service():
# return UsersService(UsersRepository)
5 changes: 3 additions & 2 deletions backend/src/game/units/tasks/questions/answers/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ class __AnswerBase(BaseModel):


class AnswerOptionRead(__AnswerBase):
id: UUID
question_id: UUID


class AnswerOptionCreate(__AnswerBase):
pass
is_correct: bool


class AnswerOptionUpdate(__AnswerBase):
answer: str | None = None
question_id: UUID | None = None
is_correct: bool | None = None
4 changes: 2 additions & 2 deletions backend/src/game/units/tasks/questions/enums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import Enum, auto
from enum import auto, StrEnum


class QuestionTypes(Enum):
class QuestionTypes(StrEnum):
SingleChoice = auto()
MultipleChoice = auto()
4 changes: 2 additions & 2 deletions backend/src/game/units/tasks/questions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ class Question(BaseModel):
type: Mapped[QuestionTypes] = mapped_column(
postgresql.ENUM(QuestionTypes, name='question_types'), nullable=False, default=QuestionTypes.SingleChoice)
question: Mapped[str] = mapped_column(String, nullable=False, default="Вопрос!")
correct_answer_id: Mapped[uuid.UUID] = mapped_column(UUID, default=uuid.uuid4)

task: Mapped["TaskUnit"] = relationship(back_populates='questions')
possible_answers: Mapped[list["AnswerOption"]] = relationship(back_populates='question', lazy='selectin')

def to_read_schema(self) -> QuestionRead:
return QuestionRead(id=self.id,
type=self.type,
task_id=self.task_id,
question=self.question,
possible_answers=self.possible_answers)
possible_answers=[model.to_read_schema() for model in self.possible_answers])
44 changes: 44 additions & 0 deletions backend/src/game/units/tasks/questions/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from uuid import UUID

from fastapi import APIRouter

from game.units.tasks.questions.schemas import QuestionRead, QuestionCreate, QuestionUpdate
from utils.types import QuestionServiceType

router = APIRouter(tags=["Questions"])


@router.get('/questions/', tags=["Dev"])
async def root(question_service: QuestionServiceType) -> list[QuestionRead]:
return await question_service.get_all()


@router.post('/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/')
async def post_question_to_task_unit(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
question_create: QuestionCreate,
question_service: QuestionServiceType) -> QuestionRead:
return await question_service.create_one(task_id, question_create)


@router.delete("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/questions/{question_id}/")
async def delete_question_from_task_unit(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
question_id: UUID,
question_service: QuestionServiceType) -> QuestionRead:
return await question_service.delete_one(question_id)


@router.patch("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/questions/{question_id}/")
async def update_question_in_task_unit(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
question_id: UUID,
question_update: QuestionUpdate,
question_service: QuestionServiceType) -> QuestionRead:
return await question_service.update_one(question_id, question_update)
4 changes: 2 additions & 2 deletions backend/src/game/units/tasks/questions/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@


class __QuestionBase(BaseModel):
task_id: UUID
type: QuestionTypes
question: str

model_config = ConfigDict(from_attributes=True)


class QuestionRead(__QuestionBase):
task_id: UUID
id: UUID
possible_answers: list[AnswerOptionRead]


class QuestionCreate(__QuestionBase):
correct_answer: AnswerOptionCreate
type: QuestionTypes = QuestionTypes.SingleChoice
possible_answers: list[AnswerOptionCreate]


Expand Down
46 changes: 23 additions & 23 deletions backend/src/game/units/tasks/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,38 @@ async def root(task_unit_service: TaskUnitServiceType) -> list[TaskUnitRead]:
return await task_unit_service.get_all()


@router.get("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/")
async def get_task_in_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
return await task_unit_service.get_one(task_id)
# @router.get("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/")
# async def get_task_in_level(map_id: UUID,
# module_id: UUID,
# level_id: UUID,
# task_id: UUID,
# task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
# return await task_unit_service.get_one(task_id)


@router.post("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/")
async def post_task_to_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_create: TaskUnitCreate,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
async def post_task_unit_to_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_create: TaskUnitCreate,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
return await task_unit_service.create_one(level_id, task_create)


@router.delete("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/")
async def delete_task_from_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
async def delete_task_unit_from_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
return await task_unit_service.delete_one(task_id)


@router.patch("/maps/{map_id}/modules/{module_id}/levels/{level_id}/tasks/{task_id}/")
async def update_task_in_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
task_update: TaskUnitUpdate,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
async def update_task_unit_in_level(map_id: UUID,
module_id: UUID,
level_id: UUID,
task_id: UUID,
task_update: TaskUnitUpdate,
task_unit_service: TaskUnitServiceType) -> TaskUnitRead:
return await task_unit_service.update_one(task_id, task_update)
14 changes: 7 additions & 7 deletions backend/src/game/units/theory/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ async def root(theory_unit_service: TheoryUnitServiceType) -> list[TheoryUnitRea
return await theory_unit_service.get_all()


@router.get("/maps/{map_id}/modules/{module_id}/levels/{level_id}/theory/{theory_id}/")
async def get_level_theory_unit(map_id: UUID,
module_id: UUID,
level_id: UUID,
theory_id: UUID,
theory_unit_service: TheoryUnitServiceType) -> TheoryUnitRead:
return await theory_unit_service.get_one(theory_id)
# @router.get("/maps/{map_id}/modules/{module_id}/levels/{level_id}/theory/{theory_id}/")
# async def get_level_theory_unit(map_id: UUID,
# module_id: UUID,
# level_id: UUID,
# theory_id: UUID,
# theory_unit_service: TheoryUnitServiceType) -> TheoryUnitRead:
# return await theory_unit_service.get_one(theory_id)


@router.post("/maps/{map_id}/modules/{module_id}/levels/{level_id}/theory/")
Expand Down
4 changes: 3 additions & 1 deletion backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
from game.modules.router import router as modules_router
from game.map.router import router as map_router
from game.units.tasks.router import router as tasks_router
from game.units.tasks.questions.router import router as questions_router
from game.units.theory.router import router as theory_router
#

# from users.router import router as users_router
# from users.tutors.router import router as tutors_router
# from users.employees.router import router as employees_router
Expand Down Expand Up @@ -58,6 +59,7 @@ async def unprotected_route():
modules_router,
map_router,
tasks_router,
questions_router,
theory_router,
# users_router,
# tutors_router,
Expand Down
28 changes: 28 additions & 0 deletions backend/src/repository/question_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import typing
import uuid

from sqlalchemy import insert

from database import async_session_maker
from game.units.tasks.questions import Question
from game.units.tasks.questions.answers import AnswerOption
from repository.sqlalchemy_repository import SQLAlchemyRepository


class QuestionRepository(SQLAlchemyRepository):
model = Question

async def add_one(self, model: dict[str, typing.Any]) -> Question:
async with async_session_maker() as session:
possible_answers_list = model.pop('possible_answers')
question_to_add = Question(**model)

possible_answers = [AnswerOption(**answer_option_dict, question_id=question_to_add.id) for
answer_option_dict in possible_answers_list]
for answer_option in possible_answers:
question_to_add.possible_answers.append(answer_option)

session.add(question_to_add)
await session.commit()
await session.refresh(question_to_add)
return question_to_add
33 changes: 33 additions & 0 deletions backend/src/services/question_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import uuid

from game.units.tasks.questions.schemas import QuestionRead, QuestionCreate, QuestionUpdate
from repository.abstract import AbstractRepository


class QuestionService:
__question_repo: AbstractRepository

def __init__(self, map_repo: type[AbstractRepository]):
self.__question_repo = map_repo()

async def create_one(self, task_id: uuid.UUID, schema_create: QuestionCreate) -> QuestionRead:
schema_dict = schema_create.model_dump(include={'type', 'possible_answers', 'question'})
schema_dict['task_id'] = task_id
return await self.__question_repo.add_one(schema_dict)

async def get_all(self) -> list[QuestionRead]:
models = await self.__question_repo.find_all()
return [model.to_read_schema() for model in models]

async def get_one(self, id: uuid.UUID) -> QuestionRead:
res = await self.__question_repo.get_one(id)
return res.to_read_schema()

async def delete_one(self, id: uuid.UUID) -> QuestionRead:
res = await self.__question_repo.delete_one(id)
return res.to_read_schema()

async def update_one(self, id: uuid.UUID, schema_update: QuestionUpdate) -> QuestionRead:
schema_dict = schema_update.model_dump()
res = await self.__question_repo.update_one(id, schema_dict)
return res.to_read_schema()
5 changes: 4 additions & 1 deletion backend/src/utils/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

from auth.base_config import current_user, current_superuser
from database import get_async_session
from game.dependencies import map_service, module_service, level_service, task_unit_service, theory_unit_service
from game.dependencies import map_service, module_service, level_service, task_unit_service, theory_unit_service, \
question_service
from services.level_service import LevelService
from services.map_service import MapService
from services.module_service import ModuleService
from services.question_service import QuestionService
from services.task_unit_service import TaskUnitService
from services.theory_unit_service import TheoryUnitService
from users.models import User
Expand All @@ -21,6 +23,7 @@
LevelServiceType = Annotated[LevelService, Depends(level_service)]
TaskUnitServiceType = Annotated[TaskUnitService, Depends(task_unit_service)]
TheoryUnitServiceType = Annotated[TheoryUnitService, Depends(theory_unit_service)]
QuestionServiceType = Annotated[QuestionService, Depends(question_service)]

# AsyncDBSession = Annotated[AsyncSession, Depends(get_async_session)]
# QueryDBLimit = Annotated[int, Query(ge=0, le=10 ** 3)]
Expand Down

0 comments on commit 9eba3cc

Please sign in to comment.