From 2ba21e021a3f377ab6acca86dfeef7416f24c47b Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Sun, 19 Nov 2023 01:30:19 +0500 Subject: [PATCH 1/9] =?UTF-8?q?=D0=9D=D0=B5=D1=80=D0=B0=D0=B1=D0=BE=D1=87?= =?UTF-8?q?=D0=B5=D0=B5=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5,=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=B2=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B5=D1=81=D1=81=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...75\321\217_name_\320\275\320\260_title.py" | 44 +++++++++++++++++++ ...0\320\270_theme_\320\275\320\260_title.py" | 32 ++++++++++++++ backend/src/game/dependencies.py | 5 +++ backend/src/game/levels/models.py | 21 +++++++-- backend/src/game/levels/router.py | 25 ++++++----- backend/src/game/levels/schemas.py | 14 +++--- backend/src/game/modules/models.py | 12 ++--- backend/src/game/units/tasks/__init__.py | 1 + backend/src/game/units/tasks/models.py | 20 ++++++--- backend/src/game/units/tasks/schemas.py | 6 +-- backend/src/game/units/theory/models.py | 10 ++++- backend/src/repository/level_repository.py | 6 +++ backend/src/services/level_service.py | 32 ++++++++++++++ backend/src/utils/types.py | 4 +- 14 files changed, 193 insertions(+), 39 deletions(-) create mode 100644 "backend/migrations/versions/2023_11_19_0024-da06417476fd_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\203\321\200\320\276\320\262\320\275\321\217_name_\320\275\320\260_title.py" create mode 100644 "backend/migrations/versions/2023_11_19_0049-5bbffa09568b_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\202\320\265\320\276\321\200\320\270\320\270_theme_\320\275\320\260_title.py" create mode 100644 backend/src/game/units/tasks/__init__.py create mode 100644 backend/src/repository/level_repository.py create mode 100644 backend/src/services/level_service.py diff --git "a/backend/migrations/versions/2023_11_19_0024-da06417476fd_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\203\321\200\320\276\320\262\320\275\321\217_name_\320\275\320\260_title.py" "b/backend/migrations/versions/2023_11_19_0024-da06417476fd_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\203\321\200\320\276\320\262\320\275\321\217_name_\320\275\320\260_title.py" new file mode 100644 index 0000000..51c0521 --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_0024-da06417476fd_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\203\321\200\320\276\320\262\320\275\321\217_name_\320\275\320\260_title.py" @@ -0,0 +1,44 @@ +"""меняет в модели уровня name на title + +Revision ID: da06417476fd +Revises: 29ed19cdffbf +Create Date: 2023-11-19 00:24:59.794292 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'da06417476fd' +down_revision: Union[str, None] = '29ed19cdffbf' +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.add_column('levels', sa.Column('title', sa.String(length=255), nullable=False)) + op.alter_column('levels', 'module_id', + existing_type=sa.UUID(), + nullable=True) + op.drop_column('levels', 'name') + op.alter_column('modules', 'map_id', + existing_type=sa.UUID(), + nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('modules', 'map_id', + existing_type=sa.UUID(), + nullable=False) + op.add_column('levels', sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False)) + op.alter_column('levels', 'module_id', + existing_type=sa.UUID(), + nullable=False) + op.drop_column('levels', 'title') + # ### end Alembic commands ### diff --git "a/backend/migrations/versions/2023_11_19_0049-5bbffa09568b_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\202\320\265\320\276\321\200\320\270\320\270_theme_\320\275\320\260_title.py" "b/backend/migrations/versions/2023_11_19_0049-5bbffa09568b_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\202\320\265\320\276\321\200\320\270\320\270_theme_\320\275\320\260_title.py" new file mode 100644 index 0000000..bec0187 --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_0049-5bbffa09568b_\320\274\320\265\320\275\321\217\320\265\321\202_\320\262_\320\274\320\276\320\264\320\265\320\273\320\270_\321\202\320\265\320\276\321\200\320\270\320\270_theme_\320\275\320\260_title.py" @@ -0,0 +1,32 @@ +"""меняет в модели теории theme на title + +Revision ID: 5bbffa09568b +Revises: da06417476fd +Create Date: 2023-11-19 00:49:49.197801 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '5bbffa09568b' +down_revision: Union[str, None] = 'da06417476fd' +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.add_column('theory_blocks', sa.Column('title', sa.String(), nullable=False)) + op.drop_column('theory_blocks', 'theme') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('theory_blocks', sa.Column('theme', sa.VARCHAR(), autoincrement=False, nullable=False)) + op.drop_column('theory_blocks', 'title') + # ### end Alembic commands ### diff --git a/backend/src/game/dependencies.py b/backend/src/game/dependencies.py index 3843ecd..46bc76b 100644 --- a/backend/src/game/dependencies.py +++ b/backend/src/game/dependencies.py @@ -1,5 +1,7 @@ +from repository.level_repository import LevelRepository from repository.map_repository import MapRepository from repository.models_repository import ModuleRepository +from services.level_service import LevelService from services.map_service import MapService from services.module_service import ModuleService @@ -11,5 +13,8 @@ def map_service() -> MapService: def module_service() -> ModuleService: return ModuleService(ModuleRepository) + +def level_service() -> LevelService: + return LevelService(LevelRepository) # def users_service(): # return UsersService(UsersRepository) diff --git a/backend/src/game/levels/models.py b/backend/src/game/levels/models.py index 6c30930..673332b 100644 --- a/backend/src/game/levels/models.py +++ b/backend/src/game/levels/models.py @@ -4,9 +4,11 @@ from sqlalchemy import String, Boolean, UUID, ForeignKey, Enum from sqlalchemy.dialects import postgresql +from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy from sqlalchemy.orm import Mapped, mapped_column, relationship from database import BaseModel +from game.levels.schemas import LevelRead if typing.TYPE_CHECKING: from game.modules.models import Module @@ -24,12 +26,23 @@ class Level(BaseModel): __tablename__ = 'levels' id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=False) - name: Mapped[str] = mapped_column(String(length=255), nullable=False) + module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=True) + title: Mapped[str] = mapped_column(String(length=255), nullable=False) module: Mapped["Module"] = relationship(back_populates='levels') - # theory: Mapped[list["TheoryUnit"]] = relationship(secondary="level_theory_blocks", back_populates='level') - # tasks: Mapped[list["TaskUnit"]] = relationship(secondary="level_tasks", back_populates='level') + + theory_units: Mapped[list["TheoryUnit"]] = relationship( + secondary="level_theory_blocks", back_populates='level', lazy='selectin') + + task_units: Mapped[list["TaskUnit"]] = relationship( + secondary="level_tasks", back_populates='level', lazy='selectin') + + def to_read_schema(self) -> LevelRead: + return LevelRead(id=self.id, + module_id=self.module_id, + title=self.title, + theory_units_ids=[unit.to_read_schema() for unit in self.theory_units], + task_units_ids=[unit.to_read_schema() for unit in self.task_units]) class EmployeesLevel(BaseModel): diff --git a/backend/src/game/levels/router.py b/backend/src/game/levels/router.py index fbb567e..bc7544d 100644 --- a/backend/src/game/levels/router.py +++ b/backend/src/game/levels/router.py @@ -3,31 +3,36 @@ from fastapi import APIRouter from game.levels.schemas import LevelRead, LevelCreate, LevelUpdate +from utils.types import LevelServiceType router = APIRouter(prefix="/levels", tags=["Levels"]) @router.get("/") -async def root() -> list[LevelRead]: - return [] +async def root(level_service: LevelServiceType) -> list[LevelRead]: + return await level_service.get_all() @router.get("/{id}") -async def get_level(id: UUID) -> LevelRead: - return LevelRead(id=id, is_accomplished=False) +async def get_level(id: UUID, + level_service: LevelServiceType) -> LevelRead: + return await level_service.get_one(id) @router.post("/") -async def post_level(level_create: LevelCreate) -> LevelRead: - return LevelRead() +async def post_level(level_create: LevelCreate, + level_service: LevelServiceType) -> LevelRead: + return await level_service.create_one(level_create) @router.delete("/{id}") -async def delete_level(id: UUID) -> LevelRead: - return LevelRead(id=id, is_accomplished=False) +async def delete_level(id: UUID, + level_service: LevelServiceType) -> LevelRead: + return await level_service.delete_one(id) @router.patch("/{id}") async def update_level(id: UUID, - level_update: LevelUpdate) -> LevelRead: - return LevelRead(id=id, is_accomplished=True) + level_update: LevelUpdate, + level_service: LevelServiceType) -> LevelRead: + return await level_service.update_one(id, level_update) diff --git a/backend/src/game/levels/schemas.py b/backend/src/game/levels/schemas.py index 541ec60..971b613 100644 --- a/backend/src/game/levels/schemas.py +++ b/backend/src/game/levels/schemas.py @@ -2,28 +2,28 @@ from pydantic import BaseModel, ConfigDict +from game.units.tasks.schemas import TaskUnitRead +from game.units.theory.schemas import TheoryUnitRead + class __LevelBase(BaseModel): module_id: UUID title: str - theory_units_id: list[UUID] | None - task_units_id: list[UUID] | None model_config = ConfigDict(from_attributes=True) class LevelRead(__LevelBase): id: UUID - theory_units_id: list[UUID] - task_units_id: list[UUID] + theory_units: list[TheoryUnitRead] + task_units: list[TaskUnitRead] class LevelCreate(__LevelBase): - pass + theory_units: list[TheoryUnitRead] | None + task_units: list[TaskUnitRead] | None class LevelUpdate(__LevelBase): module_id: UUID | None = None title: str | None = None - theory_units_id: list[UUID] | None = None - task_units_id: list[UUID] | None = None diff --git a/backend/src/game/modules/models.py b/backend/src/game/modules/models.py index 44d43f9..2155727 100644 --- a/backend/src/game/modules/models.py +++ b/backend/src/game/modules/models.py @@ -7,8 +7,10 @@ from database import BaseModel from game.modules.schemas import ModuleRead -from game.levels import Level -from game.map import Map + +if typing.TYPE_CHECKING: + from game.levels import Level + from game.map import Map class Module(BaseModel): @@ -16,17 +18,15 @@ class Module(BaseModel): id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) title: Mapped[str] = mapped_column(String(length=255), nullable=False) - map_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('maps.id')) + map_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('maps.id'), nullable=True) previous_module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=True) next_module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=True) map: Mapped["Map"] = relationship(back_populates='modules', lazy='selectin') + levels: Mapped[list["Level"]] = relationship(back_populates='module', lazy='selectin') levels_ids: AssociationProxy[list[uuid.UUID]] = association_proxy('levels', 'id') - # next_module: Mapped["Module"] = relationship(back_populates='previous_module') - # previous_module: Mapped["Module"] = relationship(back_populates='next_module') - def to_read_schema(self) -> ModuleRead: return ModuleRead(id=self.id, title=self.title, diff --git a/backend/src/game/units/tasks/__init__.py b/backend/src/game/units/tasks/__init__.py new file mode 100644 index 0000000..d4284f4 --- /dev/null +++ b/backend/src/game/units/tasks/__init__.py @@ -0,0 +1 @@ +from .models import TaskUnit, TaskTypes, TaskStates, EmployeesTask diff --git a/backend/src/game/units/tasks/models.py b/backend/src/game/units/tasks/models.py index 51e93d6..6d70184 100644 --- a/backend/src/game/units/tasks/models.py +++ b/backend/src/game/units/tasks/models.py @@ -2,17 +2,17 @@ import enum from typing import TYPE_CHECKING -from sqlalchemy import UUID, Integer, Enum, ForeignKey, Boolean +from sqlalchemy import UUID, Integer, ForeignKey, Boolean from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, mapped_column, relationship from database import BaseModel +from .schemas import TaskUnitRead if TYPE_CHECKING: from game.levels.models import Level from questions.models import Question - class TaskTypes(enum.Enum): Test = enum.auto() @@ -28,12 +28,19 @@ class TaskUnit(BaseModel): __tablename__ = 'tasks' id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - type: Mapped[TaskTypes] = mapped_column(postgresql.ENUM(TaskTypes, name='task_types'), nullable=False, default=TaskTypes.Test) + type: Mapped[TaskTypes] = mapped_column(postgresql.ENUM(TaskTypes, name='task_types'), nullable=False, + default=TaskTypes.Test) requires_review: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) score_reward: Mapped[int] = mapped_column(Integer, nullable=False, default=1) - questions: Mapped[list["Question"]] = relationship() - level: Mapped[list["Level"]] = relationship(secondary="level_tasks") + # questions: Mapped[list["Question"]] = relationship() + level: Mapped[list["Level"]] = relationship(secondary="level_tasks", back_populates='task_units') + + def to_read_schema(self) -> TaskUnitRead: + return TaskUnitRead(id=self.id, + type=self.type, + requires_review=self.requires_review, + score_reward=self.score_reward) class EmployeesTask(BaseModel): @@ -41,4 +48,5 @@ class EmployeesTask(BaseModel): task_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('tasks.id'), primary_key=True) employee_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('employees.id'), primary_key=True) - state: Mapped[TaskStates] = mapped_column(postgresql.ENUM(TaskStates, name='task_states'), nullable=False, default=TaskStates.NotViewed) + state: Mapped[TaskStates] = mapped_column(postgresql.ENUM(TaskStates, name='task_states'), nullable=False, + default=TaskStates.NotViewed) diff --git a/backend/src/game/units/tasks/schemas.py b/backend/src/game/units/tasks/schemas.py index 656a7c3..8d6ff63 100644 --- a/backend/src/game/units/tasks/schemas.py +++ b/backend/src/game/units/tasks/schemas.py @@ -1,13 +1,14 @@ +import typing from uuid import UUID from pydantic import BaseModel, ConfigDict -from game.units.tasks.models import TaskTypes from game.units.tasks.questions.schemas import QuestionRead, QuestionCreate, QuestionUpdate +from game.units.tasks import TaskTypes class __TaskUnitBase(BaseModel): - questions: list[QuestionRead] + # questions: list[QuestionRead] type: TaskTypes score_reward: int requires_review: bool @@ -28,4 +29,3 @@ class TaskUnitUpdate(__TaskUnitBase): type: TaskTypes | None = None score_reward: int | None = None requires_review: bool | None = None - diff --git a/backend/src/game/units/theory/models.py b/backend/src/game/units/theory/models.py index 2c00a08..56ae5d5 100644 --- a/backend/src/game/units/theory/models.py +++ b/backend/src/game/units/theory/models.py @@ -6,6 +6,7 @@ from sqlalchemy_utils import URLType from database import BaseModel +from game.units.theory.schemas import TheoryUnitRead if TYPE_CHECKING: from game.levels.models import Level @@ -15,11 +16,16 @@ class TheoryUnit(BaseModel): __tablename__ = 'theory_blocks' id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - theme: Mapped[str] = mapped_column(String, nullable=False) + title: Mapped[str] = mapped_column(String, nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False, default="Текст теории") # videos: Mapped[list["TheoryVideo"]] = relationship(back_populates="theory_block") - level: Mapped[list["Level"]] = relationship(secondary="level_theory_blocks") + level: Mapped[list["Level"]] = relationship(secondary="level_theory_blocks", back_populates='theory_units') + + def to_read_model(self) -> TheoryUnitRead: + return TheoryUnitRead(id=self.id, + title=self.title, + content=self.content) class TheoryVideo(BaseModel): diff --git a/backend/src/repository/level_repository.py b/backend/src/repository/level_repository.py new file mode 100644 index 0000000..e36963a --- /dev/null +++ b/backend/src/repository/level_repository.py @@ -0,0 +1,6 @@ +from game.levels import Level +from repository.sqlalchemy_repository import SQLAlchemyRepository + + +class LevelRepository(SQLAlchemyRepository): + model = Level diff --git a/backend/src/services/level_service.py b/backend/src/services/level_service.py new file mode 100644 index 0000000..7093de2 --- /dev/null +++ b/backend/src/services/level_service.py @@ -0,0 +1,32 @@ +import uuid + +from game.levels.schemas import LevelRead, LevelCreate, LevelUpdate +from repository.abstract import AbstractRepository + + +class LevelService: + __level_repo: AbstractRepository + + def __init__(self, level_repo: type[AbstractRepository]): + self.__level_repo = level_repo() + + async def create_one(self, level_create: LevelCreate) -> LevelRead: + level_dict = level_create.model_dump() + return await self.__level_repo.add_one(level_dict) + + async def get_all(self) -> list[LevelRead]: + models = await self.__level_repo.find_all() + return [model.to_read_schema() for model in models] + + async def get_one(self, id: uuid.UUID) -> LevelRead: + res = await self.__level_repo.get_one(id) + return res.to_read_schema() + + async def delete_one(self, id: uuid.UUID) -> LevelRead: + res = await self.__level_repo.delete_one(id) + return res.to_read_schema() + + async def update_one(self, id: uuid.UUID, level_update: LevelUpdate) -> LevelRead: + level_dict = level_update.model_dump() + res = await self.__level_repo.update_one(id, level_dict) + return res.to_read_schema() diff --git a/backend/src/utils/types.py b/backend/src/utils/types.py index 530dd1c..55a25bc 100644 --- a/backend/src/utils/types.py +++ b/backend/src/utils/types.py @@ -5,7 +5,8 @@ from auth.base_config import current_user, current_superuser from database import get_async_session -from game.dependencies import map_service, module_service +from game.dependencies import map_service, module_service, level_service +from services.level_service import LevelService from services.map_service import MapService from services.module_service import ModuleService from users.models import User @@ -15,6 +16,7 @@ MapServiceType = Annotated[MapService, Depends(map_service)] ModuleServiceType = Annotated[ModuleService, Depends(module_service)] +LevelServiceType = Annotated[LevelService, Depends(level_service)] AsyncDBSession = Annotated[AsyncSession, Depends(get_async_session)] QueryDBLimit = Annotated[int, Query(ge=0, le=10 ** 3)] From 35f9931675119c13e2d31aa54821f7369a3b3d27 Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Sun, 19 Nov 2023 11:00:16 +0500 Subject: [PATCH 2/9] =?UTF-8?q?=D0=A7=D1=83=D1=82=D1=8C=20=D0=B1=D0=BE?= =?UTF-8?q?=D0=BB=D0=B5=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=87=D0=B5=D0=B5?= =?UTF-8?q?=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D0=B5,=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/migrations/env.py | 4 ++-- backend/src/game/levels/enums.py | 7 +++++++ backend/src/game/levels/models.py | 12 +++--------- backend/src/game/levels/schemas.py | 5 +++-- backend/src/game/units/__init__.py | 2 ++ backend/src/game/units/tasks/__init__.py | 2 +- backend/src/game/units/tasks/enums.py | 12 ++++++++++++ backend/src/game/units/tasks/models.py | 15 +++------------ backend/src/game/units/tasks/questions/models.py | 13 +++++++------ backend/src/game/units/tasks/schemas.py | 3 +-- backend/src/game/units/theory/__init__.py | 1 + backend/src/game/units/theory/models.py | 16 ++++++++-------- backend/src/main.py | 4 ++-- 13 files changed, 52 insertions(+), 44 deletions(-) create mode 100644 backend/src/game/levels/enums.py create mode 100644 backend/src/game/units/__init__.py create mode 100644 backend/src/game/units/tasks/enums.py create mode 100644 backend/src/game/units/theory/__init__.py diff --git a/backend/migrations/env.py b/backend/migrations/env.py index 38798ad..79d9992 100644 --- a/backend/migrations/env.py +++ b/backend/migrations/env.py @@ -34,9 +34,9 @@ from game.modules.models import Module from game.levels.models import Level, LevelTheory, LevelTask from game.units.tasks.models import TaskUnit, EmployeesTask -from game.units.tasks.questions.models import Question, Answer +from game.units.tasks.questions.models import Question, AnswerOption from game.units.tasks.proofs.models import Proof, ProofVideos, ProofImages -from game.units.theory.models import TheoryUnit, TheoryVideo +from game.units.theory.models import TheoryUnit # , TheoryVideo from users.models import User from users.tutors.models import Tutor diff --git a/backend/src/game/levels/enums.py b/backend/src/game/levels/enums.py new file mode 100644 index 0000000..02d64dd --- /dev/null +++ b/backend/src/game/levels/enums.py @@ -0,0 +1,7 @@ +from enum import auto, Enum + + +class LevelStates(Enum): + NotViewed = auto() + Viewed = auto() + Completed = auto() diff --git a/backend/src/game/levels/models.py b/backend/src/game/levels/models.py index 673332b..0132daa 100644 --- a/backend/src/game/levels/models.py +++ b/backend/src/game/levels/models.py @@ -1,14 +1,14 @@ -import enum import typing import uuid -from sqlalchemy import String, Boolean, UUID, ForeignKey, Enum +from sqlalchemy import String, UUID, ForeignKey from sqlalchemy.dialects import postgresql -from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy from sqlalchemy.orm import Mapped, mapped_column, relationship from database import BaseModel +from game.levels.enums import LevelStates from game.levels.schemas import LevelRead +from game.units import TaskUnit, TheoryUnit if typing.TYPE_CHECKING: from game.modules.models import Module @@ -16,12 +16,6 @@ from game.units.theory.models import TheoryUnit -class LevelStates(enum.Enum): - NotViewed = enum.auto() - Viewed = enum.auto() - Completed = enum.auto() - - class Level(BaseModel): __tablename__ = 'levels' diff --git a/backend/src/game/levels/schemas.py b/backend/src/game/levels/schemas.py index 971b613..57f8081 100644 --- a/backend/src/game/levels/schemas.py +++ b/backend/src/game/levels/schemas.py @@ -20,8 +20,9 @@ class LevelRead(__LevelBase): class LevelCreate(__LevelBase): - theory_units: list[TheoryUnitRead] | None - task_units: list[TaskUnitRead] | None + # theory_units: list[TheoryUnitRead] | None + # task_units: list[TaskUnitRead] | None + pass class LevelUpdate(__LevelBase): diff --git a/backend/src/game/units/__init__.py b/backend/src/game/units/__init__.py new file mode 100644 index 0000000..ebdf5e2 --- /dev/null +++ b/backend/src/game/units/__init__.py @@ -0,0 +1,2 @@ +from .tasks import TaskUnit +from .theory import TheoryUnit diff --git a/backend/src/game/units/tasks/__init__.py b/backend/src/game/units/tasks/__init__.py index d4284f4..540523f 100644 --- a/backend/src/game/units/tasks/__init__.py +++ b/backend/src/game/units/tasks/__init__.py @@ -1 +1 @@ -from .models import TaskUnit, TaskTypes, TaskStates, EmployeesTask +from .models import TaskUnit, EmployeesTask diff --git a/backend/src/game/units/tasks/enums.py b/backend/src/game/units/tasks/enums.py new file mode 100644 index 0000000..35f65b1 --- /dev/null +++ b/backend/src/game/units/tasks/enums.py @@ -0,0 +1,12 @@ +from enum import Enum, auto + + +class TaskTypes(Enum): + Test = auto() + + +class TaskStates(Enum): + NotViewed = auto() + Viewed = auto() + Submitted = auto() + Finished = auto() diff --git a/backend/src/game/units/tasks/models.py b/backend/src/game/units/tasks/models.py index 6d70184..0af2b6c 100644 --- a/backend/src/game/units/tasks/models.py +++ b/backend/src/game/units/tasks/models.py @@ -1,5 +1,4 @@ import uuid -import enum from typing import TYPE_CHECKING from sqlalchemy import UUID, Integer, ForeignKey, Boolean @@ -7,22 +6,13 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from database import BaseModel +from .enums import TaskTypes, TaskStates from .schemas import TaskUnitRead if TYPE_CHECKING: from game.levels.models import Level from questions.models import Question -class TaskTypes(enum.Enum): - Test = enum.auto() - - -class TaskStates(enum.Enum): - NotViewed = enum.auto() - Viewed = enum.auto() - Submitted = enum.auto() - Finished = enum.auto() - class TaskUnit(BaseModel): __tablename__ = 'tasks' @@ -30,10 +20,11 @@ class TaskUnit(BaseModel): id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) type: Mapped[TaskTypes] = mapped_column(postgresql.ENUM(TaskTypes, name='task_types'), nullable=False, default=TaskTypes.Test) + level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), default=uuid.uuid4, nullable=True) requires_review: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) score_reward: Mapped[int] = mapped_column(Integer, nullable=False, default=1) - # questions: Mapped[list["Question"]] = relationship() + questions: Mapped[list["Question"]] = relationship(back_populates='task') level: Mapped[list["Level"]] = relationship(secondary="level_tasks", back_populates='task_units') def to_read_schema(self) -> TaskUnitRead: diff --git a/backend/src/game/units/tasks/questions/models.py b/backend/src/game/units/tasks/questions/models.py index 4447494..00e46da 100644 --- a/backend/src/game/units/tasks/questions/models.py +++ b/backend/src/game/units/tasks/questions/models.py @@ -2,7 +2,7 @@ import enum from typing import TYPE_CHECKING -from sqlalchemy import UUID, Enum, String +from sqlalchemy import UUID, Enum, String, ForeignKey from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -21,21 +21,22 @@ class Question(BaseModel): __tablename__ = 'questions' id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) + task_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('tasks.id'), default=uuid.uuid4, nullable=True) 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["Answer"]] = relationship(back_populates='question') - correct_answers: Mapped[list["Answer"]] = relationship(back_populates='question') + possible_answers: Mapped[list["AnswerOption"]] = relationship(back_populates='question') + correct_answers: Mapped[list["AnswerOption"]] = relationship(back_populates='question') -class Answer(BaseModel): +class AnswerOption(BaseModel): __tablename__ = 'answers' id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - question_id: Mapped[uuid.UUID] = mapped_column(UUID, default=uuid.uuid4) + question_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('questions.id'), default=uuid.uuid4) content: Mapped[str] = mapped_column(String, nullable=False, default='Ответ?') - question: Mapped["Question"] = relationship(back_populates='answers') + question: Mapped["Question"] = relationship(back_populates='possible_answers') diff --git a/backend/src/game/units/tasks/schemas.py b/backend/src/game/units/tasks/schemas.py index 8d6ff63..5475148 100644 --- a/backend/src/game/units/tasks/schemas.py +++ b/backend/src/game/units/tasks/schemas.py @@ -1,10 +1,9 @@ -import typing from uuid import UUID from pydantic import BaseModel, ConfigDict from game.units.tasks.questions.schemas import QuestionRead, QuestionCreate, QuestionUpdate -from game.units.tasks import TaskTypes +from game.units.tasks.enums import TaskTypes class __TaskUnitBase(BaseModel): diff --git a/backend/src/game/units/theory/__init__.py b/backend/src/game/units/theory/__init__.py new file mode 100644 index 0000000..fba643e --- /dev/null +++ b/backend/src/game/units/theory/__init__.py @@ -0,0 +1 @@ +from .models import TheoryUnit diff --git a/backend/src/game/units/theory/models.py b/backend/src/game/units/theory/models.py index 56ae5d5..17e23e3 100644 --- a/backend/src/game/units/theory/models.py +++ b/backend/src/game/units/theory/models.py @@ -28,11 +28,11 @@ def to_read_model(self) -> TheoryUnitRead: content=self.content) -class TheoryVideo(BaseModel): - __tablename__ = 'theory_videos' - - id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - theory_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('theory_blocks.id'), nullable=False) - url: Mapped[URL] = mapped_column(URLType, nullable=False) - - theory: Mapped["TheoryUnit"] = relationship(back_populates="theory_video") +# class TheoryVideo(BaseModel): +# __tablename__ = 'theory_videos' +# +# id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) +# theory_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('theory_blocks.id'), nullable=False) +# url: Mapped[URL] = mapped_column(URLType, nullable=False) +# +# theory: Mapped["TheoryUnit"] = relationship(back_populates="theory_video") diff --git a/backend/src/main.py b/backend/src/main.py index ed5c7a0..40a7b40 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -2,7 +2,7 @@ from fastapi.responses import RedirectResponse from fastapi.middleware.cors import CORSMiddleware -# from game.levels.router import router as levels_router +from game.levels.router import router as levels_router 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 @@ -54,7 +54,7 @@ async def unprotected_route(): include_routers( - # levels_router, + levels_router, modules_router, map_router, # tasks_router, From cbcb7d7f4221474ea978fc6e8a72510d1fe0db73 Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Sun, 19 Nov 2023 21:52:41 +0500 Subject: [PATCH 3/9] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=87=D0=B5=D0=B5?= =?UTF-8?q?=20api=20=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/game/levels/models.py | 6 +++--- backend/src/game/modules/models.py | 2 +- backend/src/repository/sqlalchemy_repository.py | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/game/levels/models.py b/backend/src/game/levels/models.py index 0132daa..5dedf78 100644 --- a/backend/src/game/levels/models.py +++ b/backend/src/game/levels/models.py @@ -20,7 +20,7 @@ class Level(BaseModel): __tablename__ = 'levels' id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=True) + module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id')) title: Mapped[str] = mapped_column(String(length=255), nullable=False) module: Mapped["Module"] = relationship(back_populates='levels') @@ -35,8 +35,8 @@ def to_read_schema(self) -> LevelRead: return LevelRead(id=self.id, module_id=self.module_id, title=self.title, - theory_units_ids=[unit.to_read_schema() for unit in self.theory_units], - task_units_ids=[unit.to_read_schema() for unit in self.task_units]) + theory_units=[unit.to_read_schema() for unit in self.theory_units], + task_units=[unit.to_read_schema() for unit in self.task_units]) class EmployeesLevel(BaseModel): diff --git a/backend/src/game/modules/models.py b/backend/src/game/modules/models.py index 2155727..b82f667 100644 --- a/backend/src/game/modules/models.py +++ b/backend/src/game/modules/models.py @@ -18,7 +18,7 @@ class Module(BaseModel): id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) title: Mapped[str] = mapped_column(String(length=255), nullable=False) - map_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('maps.id'), nullable=True) + map_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('maps.id')) previous_module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=True) next_module_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('modules.id'), nullable=True) diff --git a/backend/src/repository/sqlalchemy_repository.py b/backend/src/repository/sqlalchemy_repository.py index 5afaa9a..1c2e380 100644 --- a/backend/src/repository/sqlalchemy_repository.py +++ b/backend/src/repository/sqlalchemy_repository.py @@ -37,6 +37,7 @@ async def delete_one(self, id: uuid.UUID) -> model: return res.scalar_one() async def update_one(self, id: uuid.UUID, model: dict[str, typing.Any]) -> model: + model = {key: value for key, value in model.items() if value} async with async_session_maker() as session: stmt = update(self.model).where(self.model.id == id).values(**model).returning(self.model) res = await session.execute(stmt) From 032f9e301a6ee81a523d96669efdf9d52197642f Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Sun, 19 Nov 2023 21:52:59 +0500 Subject: [PATCH 4/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\265\321\202\320\260\320\274\320\270.py" | 36 +++++++++++++++++ ...\201\320\260\320\274\320\270_\320\270_.py" | 30 ++++++++++++++ ...20\265\320\276\321\200\320\270\320\270.py" | 36 +++++++++++++++++ ...\267\320\260\320\275\321\213_\320\272_.py" | 40 +++++++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 "backend/migrations/versions/2023_11_19_1035-43a526a12009_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\267\320\260\320\264\320\260\321\207\320\265\320\271_\320\270_\320\276\321\202\320\262\320\265\321\202\320\260\320\274\320\270.py" create mode 100644 "backend/migrations/versions/2023_11_19_1037-e21d5fb84a66_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\262\320\276\320\277\321\200\320\276\321\201\320\260\320\274\320\270_\320\270_.py" create mode 100644 "backend/migrations/versions/2023_11_19_1048-e8496d8a36fc_\320\262\321\200\320\265\320\274\320\265\320\275\320\275\320\276_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\321\202\320\260\320\261\320\273\320\270\321\206\321\203_\321\201_\320\262\320\270\320\264\320\265\320\276_\321\202\320\265\320\276\321\200\320\270\320\270.py" create mode 100644 "backend/migrations/versions/2023_11_19_2146-4e3cf7a2acb2_\320\274\320\276\320\264\321\203\320\273\321\214_\320\270_\321\203\321\200\320\276\320\262\320\275\320\270_\320\276\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\320\276_\320\277\321\200\320\270\320\262\321\217\320\267\320\260\320\275\321\213_\320\272_.py" diff --git "a/backend/migrations/versions/2023_11_19_1035-43a526a12009_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\267\320\260\320\264\320\260\321\207\320\265\320\271_\320\270_\320\276\321\202\320\262\320\265\321\202\320\260\320\274\320\270.py" "b/backend/migrations/versions/2023_11_19_1035-43a526a12009_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\267\320\260\320\264\320\260\321\207\320\265\320\271_\320\270_\320\276\321\202\320\262\320\265\321\202\320\260\320\274\320\270.py" new file mode 100644 index 0000000..9d127b3 --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_1035-43a526a12009_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\267\320\260\320\264\320\260\321\207\320\265\320\271_\320\270_\320\276\321\202\320\262\320\265\321\202\320\260\320\274\320\270.py" @@ -0,0 +1,36 @@ +"""добавляет связь между задачей и ответами + +Revision ID: 43a526a12009 +Revises: 5bbffa09568b +Create Date: 2023-11-19 10:35:20.123826 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '43a526a12009' +down_revision: Union[str, None] = '5bbffa09568b' +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.add_column('questions', sa.Column('task_id', sa.UUID(), nullable=True)) + op.create_foreign_key(None, 'questions', 'tasks', ['task_id'], ['id']) + op.add_column('tasks', sa.Column('level_id', sa.UUID(), nullable=True)) + op.create_foreign_key(None, 'tasks', 'levels', ['level_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'tasks', type_='foreignkey') + op.drop_column('tasks', 'level_id') + op.drop_constraint(None, 'questions', type_='foreignkey') + op.drop_column('questions', 'task_id') + # ### end Alembic commands ### diff --git "a/backend/migrations/versions/2023_11_19_1037-e21d5fb84a66_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\262\320\276\320\277\321\200\320\276\321\201\320\260\320\274\320\270_\320\270_.py" "b/backend/migrations/versions/2023_11_19_1037-e21d5fb84a66_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\262\320\276\320\277\321\200\320\276\321\201\320\260\320\274\320\270_\320\270_.py" new file mode 100644 index 0000000..0875070 --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_1037-e21d5fb84a66_\320\264\320\276\320\261\320\260\320\262\320\273\321\217\320\265\321\202_\321\201\320\262\321\217\320\267\321\214_\320\274\320\265\320\266\320\264\321\203_\320\262\320\276\320\277\321\200\320\276\321\201\320\260\320\274\320\270_\320\270_.py" @@ -0,0 +1,30 @@ +"""добавляет связь между вопросами и ответами + +Revision ID: e21d5fb84a66 +Revises: 43a526a12009 +Create Date: 2023-11-19 10:37:12.398132 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e21d5fb84a66' +down_revision: Union[str, None] = '43a526a12009' +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.create_foreign_key(None, 'answers', 'questions', ['question_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'answers', type_='foreignkey') + # ### end Alembic commands ### diff --git "a/backend/migrations/versions/2023_11_19_1048-e8496d8a36fc_\320\262\321\200\320\265\320\274\320\265\320\275\320\275\320\276_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\321\202\320\260\320\261\320\273\320\270\321\206\321\203_\321\201_\320\262\320\270\320\264\320\265\320\276_\321\202\320\265\320\276\321\200\320\270\320\270.py" "b/backend/migrations/versions/2023_11_19_1048-e8496d8a36fc_\320\262\321\200\320\265\320\274\320\265\320\275\320\275\320\276_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\321\202\320\260\320\261\320\273\320\270\321\206\321\203_\321\201_\320\262\320\270\320\264\320\265\320\276_\321\202\320\265\320\276\321\200\320\270\320\270.py" new file mode 100644 index 0000000..f7391d6 --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_1048-e8496d8a36fc_\320\262\321\200\320\265\320\274\320\265\320\275\320\275\320\276_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\321\202\320\260\320\261\320\273\320\270\321\206\321\203_\321\201_\320\262\320\270\320\264\320\265\320\276_\321\202\320\265\320\276\321\200\320\270\320\270.py" @@ -0,0 +1,36 @@ +"""временно удаляет таблицу с видео теории + +Revision ID: e8496d8a36fc +Revises: e21d5fb84a66 +Create Date: 2023-11-19 10:48:35.457329 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e8496d8a36fc' +down_revision: Union[str, None] = 'e21d5fb84a66' +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_table('theory_videos') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('theory_videos', + sa.Column('id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('theory_id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('url', sa.TEXT(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['theory_id'], ['theory_blocks.id'], name='theory_videos_theory_id_fkey'), + sa.PrimaryKeyConstraint('id', name='theory_videos_pkey') + ) + # ### end Alembic commands ### diff --git "a/backend/migrations/versions/2023_11_19_2146-4e3cf7a2acb2_\320\274\320\276\320\264\321\203\320\273\321\214_\320\270_\321\203\321\200\320\276\320\262\320\275\320\270_\320\276\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\320\276_\320\277\321\200\320\270\320\262\321\217\320\267\320\260\320\275\321\213_\320\272_.py" "b/backend/migrations/versions/2023_11_19_2146-4e3cf7a2acb2_\320\274\320\276\320\264\321\203\320\273\321\214_\320\270_\321\203\321\200\320\276\320\262\320\275\320\270_\320\276\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\320\276_\320\277\321\200\320\270\320\262\321\217\320\267\320\260\320\275\321\213_\320\272_.py" new file mode 100644 index 0000000..29d83d9 --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_2146-4e3cf7a2acb2_\320\274\320\276\320\264\321\203\320\273\321\214_\320\270_\321\203\321\200\320\276\320\262\320\275\320\270_\320\276\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\320\276_\320\277\321\200\320\270\320\262\321\217\320\267\320\260\320\275\321\213_\320\272_.py" @@ -0,0 +1,40 @@ +"""модуль и уровни обязательно привязаны к родителю + +Revision ID: 4e3cf7a2acb2 +Revises: e8496d8a36fc +Create Date: 2023-11-19 21:46:34.041826 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '4e3cf7a2acb2' +down_revision: Union[str, None] = 'e8496d8a36fc' +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.alter_column('levels', 'module_id', + existing_type=sa.UUID(), + nullable=False) + op.alter_column('modules', 'map_id', + existing_type=sa.UUID(), + nullable=False) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('modules', 'map_id', + existing_type=sa.UUID(), + nullable=True) + op.alter_column('levels', 'module_id', + existing_type=sa.UUID(), + nullable=True) + # ### end Alembic commands ### From 75f867077f4021167047c06af35a8dbd0b3b0939 Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Sun, 19 Nov 2023 22:48:40 +0500 Subject: [PATCH 5/9] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=20api=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D1=81=20taskUnit'=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/migrations/env.py | 13 +++--- ...\276\321\201\320\276\320\262_\320\270_.py" | 40 +++++++++++++++++++ ...21\202\320\262\320\265\321\202\320\260.py" | 32 +++++++++++++++ backend/src/game/dependencies.py | 8 +++- backend/src/game/levels/__init__.py | 2 +- backend/src/game/units/tasks/enums.py | 4 +- backend/src/game/units/tasks/models.py | 12 +++--- .../game/units/tasks/questions/__init__.py | 1 + .../units/tasks/questions/answers/__init__.py | 1 + .../units/tasks/questions/answers/models.py | 27 +++++++++++++ .../units/tasks/questions/answers/schemas.py | 20 ++++++++++ .../src/game/units/tasks/questions/enums.py | 6 +++ .../src/game/units/tasks/questions/models.py | 30 +++++--------- .../src/game/units/tasks/questions/schemas.py | 33 +++------------ backend/src/game/units/tasks/router.py | 29 +++++++------- backend/src/game/units/tasks/schemas.py | 8 ++-- backend/src/game/units/theory/models.py | 1 + backend/src/main.py | 4 +- ...els_repository.py => module_repository.py} | 0 .../src/repository/sqlalchemy_repository.py | 2 +- .../src/repository/task_unit_repository.py | 6 +++ backend/src/services/task_unit_service.py | 34 ++++++++++++++++ backend/src/utils/types.py | 11 ++--- 23 files changed, 238 insertions(+), 86 deletions(-) create mode 100644 "backend/migrations/versions/2023_11_19_2220-e545a099be25_\320\276\320\261\320\275\320\276\320\262\320\273\321\217\320\265\321\202_\320\274\320\276\320\264\320\265\320\273\320\270_\320\267\320\260\320\264\320\260\321\207\320\270_\320\262\320\276\320\277\321\200\320\276\321\201\320\276\320\262_\320\270_.py" create mode 100644 "backend/migrations/versions/2023_11_19_2230-842b8bf56cef_\320\274\320\265\320\275\321\217\320\265\321\202_\320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265_\320\272\320\276\320\273\320\276\320\275\320\272\320\270_\321\203_\320\276\321\202\320\262\320\265\321\202\320\260.py" create mode 100644 backend/src/game/units/tasks/questions/__init__.py create mode 100644 backend/src/game/units/tasks/questions/answers/__init__.py create mode 100644 backend/src/game/units/tasks/questions/answers/models.py create mode 100644 backend/src/game/units/tasks/questions/answers/schemas.py create mode 100644 backend/src/game/units/tasks/questions/enums.py rename backend/src/repository/{models_repository.py => module_repository.py} (100%) create mode 100644 backend/src/repository/task_unit_repository.py create mode 100644 backend/src/services/task_unit_service.py diff --git a/backend/migrations/env.py b/backend/migrations/env.py index 79d9992..8f5c0c9 100644 --- a/backend/migrations/env.py +++ b/backend/migrations/env.py @@ -30,13 +30,14 @@ # from myapp import mymodel # target_metadata = mymodel.Base.metadata -from game.map.models import Map -from game.modules.models import Module -from game.levels.models import Level, LevelTheory, LevelTask -from game.units.tasks.models import TaskUnit, EmployeesTask -from game.units.tasks.questions.models import Question, AnswerOption +from game.map import Map +from game.modules import Module +from game.levels import Level, LevelTheory, LevelTask +from game.units.tasks import TaskUnit, EmployeesTask +from game.units.theory import TheoryUnit # , TheoryVideo +from game.units.tasks.questions import Question +from game.units.tasks.questions.answers import AnswerOption from game.units.tasks.proofs.models import Proof, ProofVideos, ProofImages -from game.units.theory.models import TheoryUnit # , TheoryVideo from users.models import User from users.tutors.models import Tutor diff --git "a/backend/migrations/versions/2023_11_19_2220-e545a099be25_\320\276\320\261\320\275\320\276\320\262\320\273\321\217\320\265\321\202_\320\274\320\276\320\264\320\265\320\273\320\270_\320\267\320\260\320\264\320\260\321\207\320\270_\320\262\320\276\320\277\321\200\320\276\321\201\320\276\320\262_\320\270_.py" "b/backend/migrations/versions/2023_11_19_2220-e545a099be25_\320\276\320\261\320\275\320\276\320\262\320\273\321\217\320\265\321\202_\320\274\320\276\320\264\320\265\320\273\320\270_\320\267\320\260\320\264\320\260\321\207\320\270_\320\262\320\276\320\277\321\200\320\276\321\201\320\276\320\262_\320\270_.py" new file mode 100644 index 0000000..a46d81e --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_2220-e545a099be25_\320\276\320\261\320\275\320\276\320\262\320\273\321\217\320\265\321\202_\320\274\320\276\320\264\320\265\320\273\320\270_\320\267\320\260\320\264\320\260\321\207\320\270_\320\262\320\276\320\277\321\200\320\276\321\201\320\276\320\262_\320\270_.py" @@ -0,0 +1,40 @@ +"""обновляет модели задачи, вопросов и ответов + +Revision ID: e545a099be25 +Revises: 4e3cf7a2acb2 +Create Date: 2023-11-19 22:20:11.830596 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e545a099be25' +down_revision: Union[str, None] = '4e3cf7a2acb2' +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.add_column('answers', sa.Column('is_correct', sa.Boolean(), nullable=False)) + op.alter_column('tasks', 'level_id', + existing_type=sa.UUID(), + nullable=False) + op.add_column('theory_blocks', sa.Column('level_id', sa.UUID(), nullable=False)) + op.create_foreign_key(None, 'theory_blocks', 'levels', ['level_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'theory_blocks', type_='foreignkey') + op.drop_column('theory_blocks', 'level_id') + op.alter_column('tasks', 'level_id', + existing_type=sa.UUID(), + nullable=True) + op.drop_column('answers', 'is_correct') + # ### end Alembic commands ### diff --git "a/backend/migrations/versions/2023_11_19_2230-842b8bf56cef_\320\274\320\265\320\275\321\217\320\265\321\202_\320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265_\320\272\320\276\320\273\320\276\320\275\320\272\320\270_\321\203_\320\276\321\202\320\262\320\265\321\202\320\260.py" "b/backend/migrations/versions/2023_11_19_2230-842b8bf56cef_\320\274\320\265\320\275\321\217\320\265\321\202_\320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265_\320\272\320\276\320\273\320\276\320\275\320\272\320\270_\321\203_\320\276\321\202\320\262\320\265\321\202\320\260.py" new file mode 100644 index 0000000..69844cd --- /dev/null +++ "b/backend/migrations/versions/2023_11_19_2230-842b8bf56cef_\320\274\320\265\320\275\321\217\320\265\321\202_\320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265_\320\272\320\276\320\273\320\276\320\275\320\272\320\270_\321\203_\320\276\321\202\320\262\320\265\321\202\320\260.py" @@ -0,0 +1,32 @@ +"""меняет название колонки у ответа + +Revision ID: 842b8bf56cef +Revises: e545a099be25 +Create Date: 2023-11-19 22:30:13.120331 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '842b8bf56cef' +down_revision: Union[str, None] = 'e545a099be25' +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.add_column('answers', sa.Column('answer', sa.String(), nullable=False)) + op.drop_column('answers', 'content') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('answers', sa.Column('content', sa.VARCHAR(), autoincrement=False, nullable=False)) + op.drop_column('answers', 'answer') + # ### end Alembic commands ### diff --git a/backend/src/game/dependencies.py b/backend/src/game/dependencies.py index 46bc76b..22eab64 100644 --- a/backend/src/game/dependencies.py +++ b/backend/src/game/dependencies.py @@ -1,9 +1,11 @@ from repository.level_repository import LevelRepository from repository.map_repository import MapRepository -from repository.models_repository import ModuleRepository +from repository.module_repository import ModuleRepository +from repository.task_unit_repository import TaskUnitRepository from services.level_service import LevelService from services.map_service import MapService from services.module_service import ModuleService +from services.task_unit_service import TaskUnitService def map_service() -> MapService: @@ -16,5 +18,9 @@ def module_service() -> ModuleService: def level_service() -> LevelService: return LevelService(LevelRepository) + + +def task_unit_service() -> TaskUnitService: + return TaskUnitService(TaskUnitRepository) # def users_service(): # return UsersService(UsersRepository) diff --git a/backend/src/game/levels/__init__.py b/backend/src/game/levels/__init__.py index efc3131..427cecd 100644 --- a/backend/src/game/levels/__init__.py +++ b/backend/src/game/levels/__init__.py @@ -1 +1 @@ -from .models import Level +from .models import Level, LevelTheory, LevelTask diff --git a/backend/src/game/units/tasks/enums.py b/backend/src/game/units/tasks/enums.py index 35f65b1..5ba4f57 100644 --- a/backend/src/game/units/tasks/enums.py +++ b/backend/src/game/units/tasks/enums.py @@ -1,7 +1,7 @@ -from enum import Enum, auto +from enum import Enum, auto, StrEnum -class TaskTypes(Enum): +class TaskTypes(StrEnum): Test = auto() diff --git a/backend/src/game/units/tasks/models.py b/backend/src/game/units/tasks/models.py index 0af2b6c..8b6a5c0 100644 --- a/backend/src/game/units/tasks/models.py +++ b/backend/src/game/units/tasks/models.py @@ -1,7 +1,7 @@ import uuid from typing import TYPE_CHECKING -from sqlalchemy import UUID, Integer, ForeignKey, Boolean +from sqlalchemy import UUID, Integer, ForeignKey, Boolean, String from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -20,18 +20,20 @@ class TaskUnit(BaseModel): id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) type: Mapped[TaskTypes] = mapped_column(postgresql.ENUM(TaskTypes, name='task_types'), nullable=False, default=TaskTypes.Test) - level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), default=uuid.uuid4, nullable=True) + level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), default=uuid.uuid4) requires_review: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) score_reward: Mapped[int] = mapped_column(Integer, nullable=False, default=1) - questions: Mapped[list["Question"]] = relationship(back_populates='task') - level: Mapped[list["Level"]] = relationship(secondary="level_tasks", back_populates='task_units') + questions: Mapped[list["Question"]] = relationship(back_populates='task', lazy='selectin') + level: Mapped[list["Level"]] = relationship(secondary="level_tasks", back_populates='task_units', lazy='selectin') def to_read_schema(self) -> TaskUnitRead: return TaskUnitRead(id=self.id, + level_id=self.level_id, type=self.type, requires_review=self.requires_review, - score_reward=self.score_reward) + score_reward=self.score_reward, + questions=[model.to_read_schema() for model in self.questions]) class EmployeesTask(BaseModel): diff --git a/backend/src/game/units/tasks/questions/__init__.py b/backend/src/game/units/tasks/questions/__init__.py new file mode 100644 index 0000000..bfddaad --- /dev/null +++ b/backend/src/game/units/tasks/questions/__init__.py @@ -0,0 +1 @@ +from .models import Question diff --git a/backend/src/game/units/tasks/questions/answers/__init__.py b/backend/src/game/units/tasks/questions/answers/__init__.py new file mode 100644 index 0000000..d796dce --- /dev/null +++ b/backend/src/game/units/tasks/questions/answers/__init__.py @@ -0,0 +1 @@ +from .models import AnswerOption diff --git a/backend/src/game/units/tasks/questions/answers/models.py b/backend/src/game/units/tasks/questions/answers/models.py new file mode 100644 index 0000000..0b6698c --- /dev/null +++ b/backend/src/game/units/tasks/questions/answers/models.py @@ -0,0 +1,27 @@ +import uuid +from typing import TYPE_CHECKING + +from sqlalchemy import UUID, String, ForeignKey, Boolean +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from database import BaseModel +from game.units.tasks.questions.answers.schemas import AnswerOptionRead + +if TYPE_CHECKING: + from game.units.tasks.questions import Question + + +class AnswerOption(BaseModel): + __tablename__ = 'answers' + + id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) + question_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('questions.id'), default=uuid.uuid4) + answer: Mapped[str] = mapped_column(String, nullable=False, default='Ответ?') + is_correct: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) + + question: Mapped["Question"] = relationship(back_populates='possible_answers', lazy='selectin') + + def to_read_schema(self) -> AnswerOptionRead: + return AnswerOptionRead(id=self.id, + question_id=self.question_id, + answer=self.answer) diff --git a/backend/src/game/units/tasks/questions/answers/schemas.py b/backend/src/game/units/tasks/questions/answers/schemas.py new file mode 100644 index 0000000..a76465c --- /dev/null +++ b/backend/src/game/units/tasks/questions/answers/schemas.py @@ -0,0 +1,20 @@ +from uuid import UUID + +from pydantic import BaseModel + + +class __AnswerBase(BaseModel): + answer: str + + +class AnswerOptionRead(__AnswerBase): + question_id: UUID + + +class AnswerOptionCreate(__AnswerBase): + pass + + +class AnswerOptionUpdate(__AnswerBase): + answer: str | None = None + question_id: UUID | None = None diff --git a/backend/src/game/units/tasks/questions/enums.py b/backend/src/game/units/tasks/questions/enums.py new file mode 100644 index 0000000..d70dbd9 --- /dev/null +++ b/backend/src/game/units/tasks/questions/enums.py @@ -0,0 +1,6 @@ +from enum import Enum, auto + + +class QuestionTypes(Enum): + SingleChoice = auto() + MultipleChoice = auto() diff --git a/backend/src/game/units/tasks/questions/models.py b/backend/src/game/units/tasks/questions/models.py index 00e46da..6b29e46 100644 --- a/backend/src/game/units/tasks/questions/models.py +++ b/backend/src/game/units/tasks/questions/models.py @@ -1,20 +1,17 @@ import uuid -import enum from typing import TYPE_CHECKING -from sqlalchemy import UUID, Enum, String, ForeignKey +from sqlalchemy import UUID, String, ForeignKey from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, mapped_column, relationship from database import BaseModel +from game.units.tasks.questions.enums import QuestionTypes +from game.units.tasks.questions.schemas import QuestionRead if TYPE_CHECKING: - from game.units.tasks.models import TaskUnit - - -class QuestionTypes(enum.Enum): - SingleChoice = enum.auto() - MultipleChoice = enum.auto() + from game.units.tasks import TaskUnit + from game.units.tasks.questions.answers import AnswerOption class Question(BaseModel): @@ -28,15 +25,10 @@ class Question(BaseModel): 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') - correct_answers: Mapped[list["AnswerOption"]] = relationship(back_populates='question') - - -class AnswerOption(BaseModel): - __tablename__ = 'answers' - - id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) - question_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('questions.id'), default=uuid.uuid4) - content: Mapped[str] = mapped_column(String, nullable=False, default='Ответ?') + possible_answers: Mapped[list["AnswerOption"]] = relationship(back_populates='question', lazy='selectin') - question: Mapped["Question"] = relationship(back_populates='possible_answers') + def to_read_schema(self) -> QuestionRead: + return QuestionRead(id=self.id, + task_id=self.task_id, + question=self.question, + possible_answers=self.possible_answers) diff --git a/backend/src/game/units/tasks/questions/schemas.py b/backend/src/game/units/tasks/questions/schemas.py index ff27f9b..4e8676a 100644 --- a/backend/src/game/units/tasks/questions/schemas.py +++ b/backend/src/game/units/tasks/questions/schemas.py @@ -2,10 +2,10 @@ from pydantic import BaseModel, ConfigDict -from game.units.tasks.questions.models import QuestionTypes +from game.units.tasks.questions.answers.schemas import AnswerOptionRead, AnswerOptionCreate +from game.units.tasks.questions.enums import QuestionTypes -# region Question class __QuestionBase(BaseModel): task_id: UUID type: QuestionTypes @@ -16,36 +16,15 @@ class __QuestionBase(BaseModel): class QuestionRead(__QuestionBase): id: UUID - possible_answers: list["AnswerRead"] + possible_answers: list[AnswerOptionRead] class QuestionCreate(__QuestionBase): - correct_answer: "AnswerCreate" - possible_answers: list["AnswerCreate"] + correct_answer: AnswerOptionCreate + possible_answers: list[AnswerOptionCreate] class QuestionUpdate(__QuestionBase): type: QuestionTypes | None = None question: str | None = None - possible_answers: list["AnswerUpdate"] | None = None - - -# endregion Question - -# region Answer -class __AnswerBase(BaseModel): - content: str - - -class AnswerRead(__AnswerBase): - question_id: UUID - - -class AnswerCreate(__AnswerBase): - pass - - -class AnswerUpdate(__AnswerBase): - content: str | None = None - question_id: UUID | None = None -# endregion Answer + possible_answers: list[AnswerOptionCreate] | None = None diff --git a/backend/src/game/units/tasks/router.py b/backend/src/game/units/tasks/router.py index a922ce3..2109303 100644 --- a/backend/src/game/units/tasks/router.py +++ b/backend/src/game/units/tasks/router.py @@ -3,31 +3,32 @@ from fastapi import APIRouter from game.units.tasks.schemas import TaskUnitRead, TaskUnitCreate, TaskUnitUpdate +from utils.types import TaskUnitServiceType router = APIRouter(prefix="/tasks", tags=["Task"]) -@router.get("/") -async def root() -> list[TaskUnitRead]: - return [] - - @router.get("/{id}") -async def get_task(id: UUID) -> TaskUnitRead: - return TaskUnitRead() +async def get_task(id: UUID, + task_unit_service: TaskUnitServiceType) -> TaskUnitRead: + return await task_unit_service.get_one(id) -@router.post("/") -async def post_task(task_create: TaskUnitCreate) -> TaskUnitRead: - return TaskUnitRead() +@router.post("/level/{level_id}/") +async def post_task(level_id: UUID, + task_create: TaskUnitCreate, + task_unit_service: TaskUnitServiceType) -> TaskUnitRead: + return await task_unit_service.create_one(level_id, task_create) @router.delete("/{id}") -async def delete_task(id: UUID) -> TaskUnitRead: - return TaskUnitRead() +async def delete_task(id: UUID, + task_unit_service: TaskUnitServiceType) -> TaskUnitRead: + return await task_unit_service.delete_one(id) @router.patch("/{id}") async def update_task(id: UUID, - task_update: TaskUnitUpdate) -> TaskUnitRead: - return TaskUnitRead() + task_update: TaskUnitUpdate, + task_unit_service: TaskUnitServiceType) -> TaskUnitRead: + return await task_unit_service.update_one(id, task_update) diff --git a/backend/src/game/units/tasks/schemas.py b/backend/src/game/units/tasks/schemas.py index 5475148..cde51ce 100644 --- a/backend/src/game/units/tasks/schemas.py +++ b/backend/src/game/units/tasks/schemas.py @@ -7,7 +7,6 @@ class __TaskUnitBase(BaseModel): - # questions: list[QuestionRead] type: TaskTypes score_reward: int requires_review: bool @@ -16,15 +15,18 @@ class __TaskUnitBase(BaseModel): class TaskUnitRead(__TaskUnitBase): + questions: list[QuestionRead] + level_id: UUID id: UUID class TaskUnitCreate(__TaskUnitBase): - questions: list[QuestionCreate] + type: TaskTypes = TaskTypes.Test + score_reward: int = 1 + requires_review: bool = False class TaskUnitUpdate(__TaskUnitBase): - questions: list[QuestionUpdate] | None = None type: TaskTypes | None = None score_reward: int | None = None requires_review: bool | None = None diff --git a/backend/src/game/units/theory/models.py b/backend/src/game/units/theory/models.py index 17e23e3..fd55e8f 100644 --- a/backend/src/game/units/theory/models.py +++ b/backend/src/game/units/theory/models.py @@ -18,6 +18,7 @@ class TheoryUnit(BaseModel): id: Mapped[uuid.UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4) title: Mapped[str] = mapped_column(String, nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False, default="Текст теории") + level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), default=uuid.uuid4) # videos: Mapped[list["TheoryVideo"]] = relationship(back_populates="theory_block") level: Mapped[list["Level"]] = relationship(secondary="level_theory_blocks", back_populates='theory_units') diff --git a/backend/src/main.py b/backend/src/main.py index 40a7b40..3928b71 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -5,7 +5,7 @@ from game.levels.router import router as levels_router 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.router import router as tasks_router # from game.units.theory.router import router as theory_router # # from users.router import router as users_router @@ -57,7 +57,7 @@ async def unprotected_route(): levels_router, modules_router, map_router, - # tasks_router, + tasks_router, # theory_router, # users_router, # tutors_router, diff --git a/backend/src/repository/models_repository.py b/backend/src/repository/module_repository.py similarity index 100% rename from backend/src/repository/models_repository.py rename to backend/src/repository/module_repository.py diff --git a/backend/src/repository/sqlalchemy_repository.py b/backend/src/repository/sqlalchemy_repository.py index 1c2e380..782a380 100644 --- a/backend/src/repository/sqlalchemy_repository.py +++ b/backend/src/repository/sqlalchemy_repository.py @@ -25,7 +25,7 @@ async def find_all(self) -> list[model]: async def get_one(self, id: uuid.UUID) -> model: async with async_session_maker() as session: - stmt = select(self.model).where(self.model.id == id).order_by(self.model.title) + stmt = select(self.model).where(self.model.id == id) res = await session.execute(stmt) return res.scalar_one() diff --git a/backend/src/repository/task_unit_repository.py b/backend/src/repository/task_unit_repository.py new file mode 100644 index 0000000..9cfbb7f --- /dev/null +++ b/backend/src/repository/task_unit_repository.py @@ -0,0 +1,6 @@ +from game.units import TaskUnit +from repository.sqlalchemy_repository import SQLAlchemyRepository + + +class TaskUnitRepository(SQLAlchemyRepository): + model = TaskUnit diff --git a/backend/src/services/task_unit_service.py b/backend/src/services/task_unit_service.py new file mode 100644 index 0000000..f41e9da --- /dev/null +++ b/backend/src/services/task_unit_service.py @@ -0,0 +1,34 @@ +import uuid + +from game.modules.schemas import ModuleRead, ModuleCreate, ModuleUpdate +from game.units.tasks.schemas import TaskUnitCreate, TaskUnitRead, TaskUnitUpdate +from repository.abstract import AbstractRepository + + +class TaskUnitService: + __task_unit_repo: AbstractRepository + + def __init__(self, map_repo: type[AbstractRepository]): + self.__task_unit_repo = map_repo() + + async def create_one(self, level_id: uuid.UUID, schema_create: TaskUnitCreate) -> TaskUnitRead: + schema_dict = schema_create.model_dump() + schema_dict['level_id'] = level_id + return await self.__task_unit_repo.add_one(schema_dict) + + async def get_all(self) -> list[TaskUnitRead]: + models = await self.__task_unit_repo.find_all() + return [model.to_read_schema() for model in models] + + async def get_one(self, id: uuid.UUID) -> TaskUnitRead: + res = await self.__task_unit_repo.get_one(id) + return res.to_read_schema() + + async def delete_one(self, id: uuid.UUID) -> TaskUnitRead: + res = await self.__task_unit_repo.delete_one(id) + return res.to_read_schema() + + async def update_one(self, id: uuid.UUID, schema_update: TaskUnitUpdate) -> TaskUnitRead: + schema_dict = schema_update.model_dump() + res = await self.__task_unit_repo.update_one(id, schema_dict) + return res.to_read_schema() diff --git a/backend/src/utils/types.py b/backend/src/utils/types.py index 55a25bc..53cc9b5 100644 --- a/backend/src/utils/types.py +++ b/backend/src/utils/types.py @@ -5,10 +5,11 @@ 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 +from game.dependencies import map_service, module_service, level_service, task_unit_service from services.level_service import LevelService from services.map_service import MapService from services.module_service import ModuleService +from services.task_unit_service import TaskUnitService from users.models import User CurrentUser = Annotated[User, Depends(current_user)] @@ -17,7 +18,7 @@ MapServiceType = Annotated[MapService, Depends(map_service)] ModuleServiceType = Annotated[ModuleService, Depends(module_service)] LevelServiceType = Annotated[LevelService, Depends(level_service)] - -AsyncDBSession = Annotated[AsyncSession, Depends(get_async_session)] -QueryDBLimit = Annotated[int, Query(ge=0, le=10 ** 3)] -QueryDBOffset = Annotated[int, Query(ge=0, le=10 ** 6)] \ No newline at end of file +TaskUnitServiceType = Annotated[TaskUnitService, Depends(task_unit_service)] +# AsyncDBSession = Annotated[AsyncSession, Depends(get_async_session)] +# QueryDBLimit = Annotated[int, Query(ge=0, le=10 ** 3)] +# QueryDBOffset = Annotated[int, Query(ge=0, le=10 ** 6)] From 964323ea7e130165e2a9f7b2325e831027ba93a7 Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Mon, 20 Nov 2023 00:52:50 +0500 Subject: [PATCH 6/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=20api=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D1=81=20=D1=83=D1=80=D0=BE=D0=B2=D0=BD?= =?UTF-8?q?=D1=8F=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/game/levels/router.py | 51 +++++++++++------ backend/src/game/map/router.py | 8 +-- backend/src/game/modules/router.py | 46 +++++++++------ backend/src/game/units/tasks/router.py | 52 +++++++++++------ backend/src/game/units/theory/router.py | 75 ++++++++++++------------- 5 files changed, 134 insertions(+), 98 deletions(-) diff --git a/backend/src/game/levels/router.py b/backend/src/game/levels/router.py index bc7544d..4bcf472 100644 --- a/backend/src/game/levels/router.py +++ b/backend/src/game/levels/router.py @@ -5,34 +5,49 @@ from game.levels.schemas import LevelRead, LevelCreate, LevelUpdate from utils.types import LevelServiceType -router = APIRouter(prefix="/levels", tags=["Levels"]) +router = APIRouter(tags=["Levels"]) -@router.get("/") +@router.get("/levels/", tags=['Dev']) async def root(level_service: LevelServiceType) -> list[LevelRead]: return await level_service.get_all() -@router.get("/{id}") -async def get_level(id: UUID, - level_service: LevelServiceType) -> LevelRead: - return await level_service.get_one(id) +@router.get("/maps/{map_id}/modules/{module_id}/levels/") +async def get_module_levels(map_id: UUID, + module_id: UUID, + level_service: LevelServiceType) -> list[LevelRead]: + return await level_service.get_all() + + +@router.get("/maps/{map_id}/modules/{module_id}/levels/{level_id}/") +async def get_module_level(map_id: UUID, + module_id: UUID, + level_id: UUID, + level_service: LevelServiceType) -> LevelRead: + return await level_service.get_one(level_id) -@router.post("/") -async def post_level(level_create: LevelCreate, - level_service: LevelServiceType) -> LevelRead: +@router.post("/maps/{map_id}/modules/{module_id}/levels/") +async def post_level_to_module(map_id: UUID, + module_id: UUID, + level_create: LevelCreate, + level_service: LevelServiceType) -> LevelRead: return await level_service.create_one(level_create) -@router.delete("/{id}") -async def delete_level(id: UUID, - level_service: LevelServiceType) -> LevelRead: - return await level_service.delete_one(id) +@router.delete("/maps/{map_id}/modules/{module_id}/levels/{level_id}/") +async def delete_level_from_module(map_id: UUID, + module_id: UUID, + level_id: UUID, + level_service: LevelServiceType) -> LevelRead: + return await level_service.delete_one(level_id) -@router.patch("/{id}") -async def update_level(id: UUID, - level_update: LevelUpdate, - level_service: LevelServiceType) -> LevelRead: - return await level_service.update_one(id, level_update) +@router.patch("/maps/{map_id}/modules/{module_id}/levels/{level_id}/") +async def update_level_in_module(map_id: UUID, + module_id: UUID, + level_id: UUID, + level_update: LevelUpdate, + level_service: LevelServiceType) -> LevelRead: + return await level_service.update_one(level_id, level_update) diff --git a/backend/src/game/map/router.py b/backend/src/game/map/router.py index bd7f1e5..28e6ed6 100644 --- a/backend/src/game/map/router.py +++ b/backend/src/game/map/router.py @@ -9,11 +9,11 @@ @router.get("/") -async def root(map_service: MapServiceType) -> list[MapRead]: +async def get_maps(map_service: MapServiceType) -> list[MapRead]: return await map_service.get_all() -@router.get("/{id}") +@router.get("/{id}/") async def get_map(id: UUID, map_service: MapServiceType) -> MapRead: return await map_service.get_one(id) @@ -25,13 +25,13 @@ async def post_map(map_create: MapCreate, return await map_service.create_one(map_create) -@router.delete("/{id}") +@router.delete("/{id}/") async def delete_map(id: UUID, map_service: MapServiceType) -> MapRead: return await map_service.delete_one(id) -@router.patch("/{id}") +@router.patch("/{id}/") async def update_map(id: UUID, map_update: MapUpdate, map_service: MapServiceType) -> MapRead: diff --git a/backend/src/game/modules/router.py b/backend/src/game/modules/router.py index 998c6b3..54d4458 100644 --- a/backend/src/game/modules/router.py +++ b/backend/src/game/modules/router.py @@ -6,39 +6,49 @@ from game.modules.schemas import ModuleRead, ModuleCreate, ModuleUpdate from utils.types import ModuleServiceType -router = APIRouter(prefix="/modules", tags=["Module"]) +router = APIRouter(tags=["Module"]) -@router.get("/") +@router.get("/modules/", tags=["Dev"]) async def root(module_service: ModuleServiceType) -> list[ModuleRead]: return await module_service.get_all() -@router.get("/{id}") -async def get_module(id: UUID, - module_service: ModuleServiceType) -> ModuleRead: - return await module_service.get_one(id) +@router.get("/maps/{map_id}/modules/") +async def get_map_modules(map_id: UUID, + module_service: ModuleServiceType) -> list[ModuleRead]: + return await module_service.get_all() + + +@router.get("/maps/{map_id}/modules/{module_id}/") +async def get_map_module(map_id: UUID, + module_id: UUID, + module_service: ModuleServiceType) -> ModuleRead: + return await module_service.get_one(module_id) -@router.post("/") -async def post_module(module_create: ModuleCreate, - module_service: ModuleServiceType) -> ModuleRead: +@router.post("/maps/{map_id}/modules/") +async def post_module_to_map(map_id: UUID, + module_create: ModuleCreate, + module_service: ModuleServiceType) -> ModuleRead: return await module_service.create_one(module_create) -@router.delete("/{id}") -async def delete_module(id: UUID, - module_service: ModuleServiceType) -> ModuleRead: +@router.delete("/maps/{map_id}/modules/{module_id}/") +async def delete_module_from_map(map_id: UUID, + module_id: UUID, + module_service: ModuleServiceType) -> ModuleRead: # TODO: Добавить обработку ошибок # try: # deleted_module = # except NoResultFound as exc: # raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=exc.args) - return await module_service.delete_one(id) + return await module_service.delete_one(module_id) -@router.patch("/{id}") -async def update_module(id: UUID, - module_update: ModuleUpdate, - module_service: ModuleServiceType) -> ModuleRead: - return await module_service.update_one(id, module_update) +@router.patch("/maps/{map_id}/modules/{module_id}/") +async def update_module_on_map(map_id: UUID, + module_id: UUID, + module_update: ModuleUpdate, + module_service: ModuleServiceType) -> ModuleRead: + return await module_service.update_one(module_id, module_update) diff --git a/backend/src/game/units/tasks/router.py b/backend/src/game/units/tasks/router.py index 2109303..2a83eb6 100644 --- a/backend/src/game/units/tasks/router.py +++ b/backend/src/game/units/tasks/router.py @@ -5,30 +5,46 @@ from game.units.tasks.schemas import TaskUnitRead, TaskUnitCreate, TaskUnitUpdate from utils.types import TaskUnitServiceType -router = APIRouter(prefix="/tasks", tags=["Task"]) +router = APIRouter(tags=["Task"]) -@router.get("/{id}") -async def get_task(id: UUID, - task_unit_service: TaskUnitServiceType) -> TaskUnitRead: - return await task_unit_service.get_one(id) +@router.get("/tasks/", tags=['Dev']) +async def root(task_unit_service: TaskUnitServiceType) -> TaskUnitRead: + return await task_unit_service.get_all() -@router.post("/level/{level_id}/") -async def post_task(level_id: UUID, - task_create: TaskUnitCreate, - task_unit_service: TaskUnitServiceType) -> TaskUnitRead: +@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: return await task_unit_service.create_one(level_id, task_create) -@router.delete("/{id}") -async def delete_task(id: UUID, - task_unit_service: TaskUnitServiceType) -> TaskUnitRead: - return await task_unit_service.delete_one(id) +@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: + return await task_unit_service.delete_one(task_id) -@router.patch("/{id}") -async def update_task(id: UUID, - task_update: TaskUnitUpdate, - task_unit_service: TaskUnitServiceType) -> TaskUnitRead: - return await task_unit_service.update_one(id, task_update) +@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: + return await task_unit_service.update_one(task_id, task_update) diff --git a/backend/src/game/units/theory/router.py b/backend/src/game/units/theory/router.py index 63c9ba8..caf107f 100644 --- a/backend/src/game/units/theory/router.py +++ b/backend/src/game/units/theory/router.py @@ -2,54 +2,49 @@ from fastapi import APIRouter -from game.units.theory.schemas import (TheoryUnitRead, TheoryUnitCreate, TheoryUnitUpdate, - TheoryVideoRead, TheoryVideoCreate, TheoryVideoUpdate) +from game.units.theory.schemas import TheoryUnitRead, TheoryUnitCreate, TheoryUnitUpdate +from utils.types import TheoryUnitServiceType -router = APIRouter(prefix="/theory", tags=["Theory"]) +router = APIRouter(tags=["Theory"]) -@router.get("/") -async def root() -> list[TheoryUnitRead]: - return [] +@router.get('/theory/', tags=['Dev']) +async def root(theory_unit_service: TheoryUnitServiceType) -> list[TheoryUnitRead]: + return await theory_unit_service.get_all() -@router.get("/{id}") -async def get_theory_block(id: UUID) -> TheoryUnitRead: - return TheoryUnitRead() +@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("/videos/{id}") -async def get_theory_block_videos(id: UUID) -> list[TheoryVideoRead]: - return [] +@router.post("/maps/{map_id}/modules/{module_id}/levels/{level_id}/theory/") +async def post_theory_unit_to_level(map_id: UUID, + module_id: UUID, + level_id: UUID, + theory_create: TheoryUnitCreate, + theory_unit_service: TheoryUnitServiceType) -> TheoryUnitRead: + return await theory_unit_service.create_one(level_id, theory_create) -@router.post("/") -async def post_theory_block(theory_create: TheoryUnitCreate) -> TheoryUnitRead: - return TheoryUnitRead() +@router.delete("/maps/{map_id}/modules/{module_id}/levels/{level_id}/theory/{theory_id}/") +async def delete_theory_unit_from_level(map_id: UUID, + module_id: UUID, + level_id: UUID, + theory_id: UUID, + theory_unit_service: TheoryUnitServiceType) -> TheoryUnitRead: + return await theory_unit_service.delete_one(theory_id) -@router.post("/videos/") -async def post_theory_block_video(theory_video_create: TheoryVideoCreate) -> TheoryVideoRead: - return TheoryVideoRead() - - -@router.delete("/{id}") -async def delete_theory_block(id: UUID) -> TheoryUnitRead: - return TheoryUnitRead() - - -@router.delete("/videos/{id}") -async def delete_theory_block_video(id: UUID) -> TheoryVideoRead: - return TheoryVideoRead() - - -@router.patch("/{id}") -async def update_theory_block(id: UUID, - theory_update: TheoryUnitUpdate) -> TheoryUnitRead: - return TheoryUnitRead() - - -@router.patch("/videos/{id}") -async def update_theory_block_video(id: UUID, - theory_video_update: TheoryVideoUpdate) -> TheoryVideoRead: - return TheoryVideoRead() +@router.patch("/maps/{map_id}/modules/{module_id}/levels/{level_id}/theory/{theory_id}/") +async def update_theory_unit_in_level(map_id: UUID, + module_id: UUID, + level_id: UUID, + theory_id: UUID, + theory_update: TheoryUnitUpdate, + theory_unit_service: TheoryUnitServiceType) -> TheoryUnitRead: + return await theory_unit_service.update_one(theory_id, theory_update) From 5f3804c88bf3d2c2f875077f95495bd85c99762b Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Mon, 20 Nov 2023 00:54:14 +0500 Subject: [PATCH 7/9] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD?= =?UTF-8?q?=D0=B0=D0=BB=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D1=8B=20=D1=81=20=D1=82=D0=B5=D0=BE=D1=80=D0=B5=D1=82?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=BC=D0=B8=20=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=D0=BC=D0=B8=20=D0=BD=D0=B0=20=D1=83=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/game/dependencies.py | 6 ++++ backend/src/main.py | 4 +-- .../src/repository/theory_unit_repository.py | 6 ++++ backend/src/services/task_unit_service.py | 1 - backend/src/services/theory_unit_service.py | 33 +++++++++++++++++++ backend/src/utils/types.py | 5 ++- 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 backend/src/repository/theory_unit_repository.py create mode 100644 backend/src/services/theory_unit_service.py diff --git a/backend/src/game/dependencies.py b/backend/src/game/dependencies.py index 22eab64..5bca241 100644 --- a/backend/src/game/dependencies.py +++ b/backend/src/game/dependencies.py @@ -2,10 +2,12 @@ from repository.map_repository import MapRepository from repository.module_repository import ModuleRepository 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.task_unit_service import TaskUnitService +from services.theory_unit_service import TheoryUnitService def map_service() -> MapService: @@ -22,5 +24,9 @@ def level_service() -> LevelService: def task_unit_service() -> TaskUnitService: return TaskUnitService(TaskUnitRepository) + + +def theory_unit_service() -> TheoryUnitService: + return TheoryUnitService(TheoryUnitRepository) # def users_service(): # return UsersService(UsersRepository) diff --git a/backend/src/main.py b/backend/src/main.py index 3928b71..dc2e93b 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -6,7 +6,7 @@ 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.theory.router import router as theory_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 @@ -58,7 +58,7 @@ async def unprotected_route(): modules_router, map_router, tasks_router, - # theory_router, + theory_router, # users_router, # tutors_router, # employees_router, diff --git a/backend/src/repository/theory_unit_repository.py b/backend/src/repository/theory_unit_repository.py new file mode 100644 index 0000000..02651f5 --- /dev/null +++ b/backend/src/repository/theory_unit_repository.py @@ -0,0 +1,6 @@ +from game.units import TheoryUnit +from repository.sqlalchemy_repository import SQLAlchemyRepository + + +class TheoryUnitRepository(SQLAlchemyRepository): + model = TheoryUnit diff --git a/backend/src/services/task_unit_service.py b/backend/src/services/task_unit_service.py index f41e9da..78289c2 100644 --- a/backend/src/services/task_unit_service.py +++ b/backend/src/services/task_unit_service.py @@ -1,6 +1,5 @@ import uuid -from game.modules.schemas import ModuleRead, ModuleCreate, ModuleUpdate from game.units.tasks.schemas import TaskUnitCreate, TaskUnitRead, TaskUnitUpdate from repository.abstract import AbstractRepository diff --git a/backend/src/services/theory_unit_service.py b/backend/src/services/theory_unit_service.py new file mode 100644 index 0000000..67e0d94 --- /dev/null +++ b/backend/src/services/theory_unit_service.py @@ -0,0 +1,33 @@ +import uuid + +from game.units.theory.schemas import TheoryUnitRead, TheoryUnitCreate, TheoryUnitUpdate +from repository.abstract import AbstractRepository + + +class TheoryUnitService: + __theory_unit_repo: AbstractRepository + + def __init__(self, map_repo: type[AbstractRepository]): + self.__theory_unit_repo = map_repo() + + async def create_one(self, level_id: uuid.UUID, schema_create: TheoryUnitCreate) -> TheoryUnitRead: + schema_dict = schema_create.model_dump() + schema_dict['level_id'] = level_id + return await self.__theory_unit_repo.add_one(schema_dict) + + async def get_all(self) -> list[TheoryUnitRead]: + models = await self.__theory_unit_repo.find_all() + return [model.to_read_schema() for model in models] + + async def get_one(self, id: uuid.UUID) -> TheoryUnitRead: + res = await self.__theory_unit_repo.get_one(id) + return res.to_read_schema() + + async def delete_one(self, id: uuid.UUID) -> TheoryUnitRead: + res = await self.__theory_unit_repo.delete_one(id) + return res.to_read_schema() + + async def update_one(self, id: uuid.UUID, schema_update: TheoryUnitUpdate) -> TheoryUnitRead: + schema_dict = schema_update.model_dump() + res = await self.__theory_unit_repo.update_one(id, schema_dict) + return res.to_read_schema() diff --git a/backend/src/utils/types.py b/backend/src/utils/types.py index 53cc9b5..c53a870 100644 --- a/backend/src/utils/types.py +++ b/backend/src/utils/types.py @@ -5,11 +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 +from game.dependencies import map_service, module_service, level_service, task_unit_service, theory_unit_service from services.level_service import LevelService from services.map_service import MapService from services.module_service import ModuleService from services.task_unit_service import TaskUnitService +from services.theory_unit_service import TheoryUnitService from users.models import User CurrentUser = Annotated[User, Depends(current_user)] @@ -19,6 +20,8 @@ ModuleServiceType = Annotated[ModuleService, Depends(module_service)] LevelServiceType = Annotated[LevelService, Depends(level_service)] TaskUnitServiceType = Annotated[TaskUnitService, Depends(task_unit_service)] +TheoryUnitServiceType = Annotated[TheoryUnitService, Depends(theory_unit_service)] + # AsyncDBSession = Annotated[AsyncSession, Depends(get_async_session)] # QueryDBLimit = Annotated[int, Query(ge=0, le=10 ** 3)] # QueryDBOffset = Annotated[int, Query(ge=0, le=10 ** 6)] From 9223bcc816c65148d7464d458d8041029669e138 Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Mon, 20 Nov 2023 01:07:43 +0500 Subject: [PATCH 8/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=D1=85=D0=B5=D0=BC=D1=83=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/game/units/tasks/router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/game/units/tasks/router.py b/backend/src/game/units/tasks/router.py index 2a83eb6..ad1c8f1 100644 --- a/backend/src/game/units/tasks/router.py +++ b/backend/src/game/units/tasks/router.py @@ -9,7 +9,7 @@ @router.get("/tasks/", tags=['Dev']) -async def root(task_unit_service: TaskUnitServiceType) -> TaskUnitRead: +async def root(task_unit_service: TaskUnitServiceType) -> list[TaskUnitRead]: return await task_unit_service.get_all() From 3e08d0aaf4211deb90f5e4d7027567672579d34e Mon Sep 17 00:00:00 2001 From: codEnjoyer Date: Mon, 20 Nov 2023 01:15:02 +0500 Subject: [PATCH 9/9] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D1=8F=D0=B5?= =?UTF-8?q?=D1=82=20=D1=81=D1=85=D0=B5=D0=BC=D1=83=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D0=B5=D0=B9=20=D0=B8=20=D1=8E?= =?UTF-8?q?=D0=BD=D0=B8=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/migrations/env.py | 2 +- ...20\261\320\273\320\270\321\206\321\213.py" | 44 +++++++++++++++++++ backend/src/game/levels/__init__.py | 2 +- backend/src/game/levels/models.py | 20 +-------- backend/src/game/units/tasks/models.py | 2 +- backend/src/game/units/theory/models.py | 2 +- 6 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 "backend/migrations/versions/2023_11_20_0114-f074fb69a98a_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\320\277\321\200\320\276\320\274\320\265\320\266\321\203\321\202\320\276\321\207\320\275\321\213\320\265_\321\202\320\260\320\261\320\273\320\270\321\206\321\213.py" diff --git a/backend/migrations/env.py b/backend/migrations/env.py index 8f5c0c9..2f3c64f 100644 --- a/backend/migrations/env.py +++ b/backend/migrations/env.py @@ -32,7 +32,7 @@ from game.map import Map from game.modules import Module -from game.levels import Level, LevelTheory, LevelTask +from game.levels import Level from game.units.tasks import TaskUnit, EmployeesTask from game.units.theory import TheoryUnit # , TheoryVideo from game.units.tasks.questions import Question diff --git "a/backend/migrations/versions/2023_11_20_0114-f074fb69a98a_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\320\277\321\200\320\276\320\274\320\265\320\266\321\203\321\202\320\276\321\207\320\275\321\213\320\265_\321\202\320\260\320\261\320\273\320\270\321\206\321\213.py" "b/backend/migrations/versions/2023_11_20_0114-f074fb69a98a_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\320\277\321\200\320\276\320\274\320\265\320\266\321\203\321\202\320\276\321\207\320\275\321\213\320\265_\321\202\320\260\320\261\320\273\320\270\321\206\321\213.py" new file mode 100644 index 0000000..d221643 --- /dev/null +++ "b/backend/migrations/versions/2023_11_20_0114-f074fb69a98a_\321\203\320\264\320\260\320\273\321\217\320\265\321\202_\320\277\321\200\320\276\320\274\320\265\320\266\321\203\321\202\320\276\321\207\320\275\321\213\320\265_\321\202\320\260\320\261\320\273\320\270\321\206\321\213.py" @@ -0,0 +1,44 @@ +"""удаляет промежуточные таблицы + +Revision ID: f074fb69a98a +Revises: 842b8bf56cef +Create Date: 2023-11-20 01:14:38.556334 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'f074fb69a98a' +down_revision: Union[str, None] = '842b8bf56cef' +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_table('level_tasks') + op.drop_table('level_theory_blocks') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('level_theory_blocks', + sa.Column('level_id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('theory_id', sa.UUID(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['level_id'], ['levels.id'], name='level_theory_blocks_level_id_fkey'), + sa.ForeignKeyConstraint(['theory_id'], ['theory_blocks.id'], name='level_theory_blocks_theory_id_fkey'), + sa.PrimaryKeyConstraint('level_id', 'theory_id', name='level_theory_blocks_pkey') + ) + op.create_table('level_tasks', + sa.Column('level_id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('task_id', sa.UUID(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['level_id'], ['levels.id'], name='level_tasks_level_id_fkey'), + sa.ForeignKeyConstraint(['task_id'], ['tasks.id'], name='level_tasks_task_id_fkey'), + sa.PrimaryKeyConstraint('level_id', 'task_id', name='level_tasks_pkey') + ) + # ### end Alembic commands ### diff --git a/backend/src/game/levels/__init__.py b/backend/src/game/levels/__init__.py index 427cecd..efc3131 100644 --- a/backend/src/game/levels/__init__.py +++ b/backend/src/game/levels/__init__.py @@ -1 +1 @@ -from .models import Level, LevelTheory, LevelTask +from .models import Level diff --git a/backend/src/game/levels/models.py b/backend/src/game/levels/models.py index 5dedf78..5128188 100644 --- a/backend/src/game/levels/models.py +++ b/backend/src/game/levels/models.py @@ -25,11 +25,9 @@ class Level(BaseModel): module: Mapped["Module"] = relationship(back_populates='levels') - theory_units: Mapped[list["TheoryUnit"]] = relationship( - secondary="level_theory_blocks", back_populates='level', lazy='selectin') + theory_units: Mapped[list["TheoryUnit"]] = relationship(back_populates='level', lazy='selectin') - task_units: Mapped[list["TaskUnit"]] = relationship( - secondary="level_tasks", back_populates='level', lazy='selectin') + task_units: Mapped[list["TaskUnit"]] = relationship(back_populates='level', lazy='selectin') def to_read_schema(self) -> LevelRead: return LevelRead(id=self.id, @@ -46,17 +44,3 @@ class EmployeesLevel(BaseModel): employee_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('employees.id'), nullable=False, primary_key=True) state: Mapped[LevelStates] = mapped_column( postgresql.ENUM(LevelStates, name='level_states'), nullable=False, default=LevelStates.NotViewed) - - -class LevelTask(BaseModel): - __tablename__ = "level_tasks" - - level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), nullable=False, primary_key=True) - task_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('tasks.id'), nullable=False, primary_key=True) - - -class LevelTheory(BaseModel): - __tablename__ = "level_theory_blocks" - - level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), nullable=False, primary_key=True) - theory_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('theory_blocks.id'), nullable=False, primary_key=True) diff --git a/backend/src/game/units/tasks/models.py b/backend/src/game/units/tasks/models.py index 8b6a5c0..a0676c6 100644 --- a/backend/src/game/units/tasks/models.py +++ b/backend/src/game/units/tasks/models.py @@ -25,7 +25,7 @@ class TaskUnit(BaseModel): score_reward: Mapped[int] = mapped_column(Integer, nullable=False, default=1) questions: Mapped[list["Question"]] = relationship(back_populates='task', lazy='selectin') - level: Mapped[list["Level"]] = relationship(secondary="level_tasks", back_populates='task_units', lazy='selectin') + level: Mapped[list["Level"]] = relationship(back_populates='task_units', lazy='selectin') def to_read_schema(self) -> TaskUnitRead: return TaskUnitRead(id=self.id, diff --git a/backend/src/game/units/theory/models.py b/backend/src/game/units/theory/models.py index fd55e8f..551f326 100644 --- a/backend/src/game/units/theory/models.py +++ b/backend/src/game/units/theory/models.py @@ -21,7 +21,7 @@ class TheoryUnit(BaseModel): level_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey('levels.id'), default=uuid.uuid4) # videos: Mapped[list["TheoryVideo"]] = relationship(back_populates="theory_block") - level: Mapped[list["Level"]] = relationship(secondary="level_theory_blocks", back_populates='theory_units') + level: Mapped[list["Level"]] = relationship(back_populates='theory_units') def to_read_model(self) -> TheoryUnitRead: return TheoryUnitRead(id=self.id,