diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b6d9ddb..be75478 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: Django-app workflow on: push: - branches: [ "develop" ] + branches: [ "develop"] pull_request: branches: [ "develop" ] workflow_dispatch: @@ -22,4 +22,5 @@ jobs: - name: flake8 Lint uses: py-actions/flake8@v2 with: - path: "app" \ No newline at end of file + path: "app" + - uses: isort/isort-action@master \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py index 705f18b..025f98c 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -2,11 +2,11 @@ import os from logging.config import fileConfig +from alembic import context from dotenv import load_dotenv from sqlalchemy import pool from sqlalchemy.ext.asyncio import async_engine_from_config -from alembic import context from app.core.base import Base config = context.config diff --git a/alembic/versions/043420d3cabe_examination_model_with_relationship_.py b/alembic/versions/043420d3cabe_examination_model_with_relationship_.py index a04f7f6..ebc3597 100644 --- a/alembic/versions/043420d3cabe_examination_model_with_relationship_.py +++ b/alembic/versions/043420d3cabe_examination_model_with_relationship_.py @@ -5,9 +5,8 @@ Create Date: 2023-12-15 11:49:39.453458 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = '043420d3cabe' diff --git a/alembic/versions/12ebaaa6044c_course_tariff_relationship.py b/alembic/versions/12ebaaa6044c_course_tariff_relationship.py index 9da41f0..baeb7e9 100644 --- a/alembic/versions/12ebaaa6044c_course_tariff_relationship.py +++ b/alembic/versions/12ebaaa6044c_course_tariff_relationship.py @@ -5,9 +5,8 @@ Create Date: 2023-12-15 19:02:40.113127 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = '12ebaaa6044c' diff --git a/alembic/versions/3a82c343b806_achievement_model_with_relationship.py b/alembic/versions/3a82c343b806_achievement_model_with_relationship.py index 8a99067..109b0e3 100644 --- a/alembic/versions/3a82c343b806_achievement_model_with_relationship.py +++ b/alembic/versions/3a82c343b806_achievement_model_with_relationship.py @@ -5,9 +5,8 @@ Create Date: 2023-12-15 12:15:12.153616 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = '3a82c343b806' diff --git a/alembic/versions/4098f76764d4_initial_migration.py b/alembic/versions/4098f76764d4_initial_migration.py index ba6d15a..57cc448 100644 --- a/alembic/versions/4098f76764d4_initial_migration.py +++ b/alembic/versions/4098f76764d4_initial_migration.py @@ -5,9 +5,8 @@ Create Date: 2023-12-07 23:54:47.680798 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = '4098f76764d4' diff --git a/alembic/versions/b6230f9d49dc_course_user_relationship.py b/alembic/versions/b6230f9d49dc_course_user_relationship.py index 2c6ed27..b945cdb 100644 --- a/alembic/versions/b6230f9d49dc_course_user_relationship.py +++ b/alembic/versions/b6230f9d49dc_course_user_relationship.py @@ -5,9 +5,8 @@ Create Date: 2023-12-15 18:51:03.288013 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = 'b6230f9d49dc' diff --git a/alembic/versions/d1e8da8e858b_course_task_relationship.py b/alembic/versions/d1e8da8e858b_course_task_relationship.py index b8a20dc..130e2d9 100644 --- a/alembic/versions/d1e8da8e858b_course_task_relationship.py +++ b/alembic/versions/d1e8da8e858b_course_task_relationship.py @@ -5,9 +5,8 @@ Create Date: 2023-12-15 19:21:08.180548 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = 'd1e8da8e858b' diff --git a/alembic/versions/da73f8287221_notification_model_with_relationship.py b/alembic/versions/da73f8287221_notification_model_with_relationship.py index 3dfa826..3894a4c 100644 --- a/alembic/versions/da73f8287221_notification_model_with_relationship.py +++ b/alembic/versions/da73f8287221_notification_model_with_relationship.py @@ -5,9 +5,8 @@ Create Date: 2023-12-15 10:55:10.899967 """ -from alembic import op import sqlalchemy as sa - +from alembic import op # revision identifiers, used by Alembic. revision = 'da73f8287221' diff --git a/app/api/endpoints/__init__.py b/app/api/endpoints/__init__.py index 1041bbb..e3864a2 100644 --- a/app/api/endpoints/__init__.py +++ b/app/api/endpoints/__init__.py @@ -1,9 +1,9 @@ -from .user import router as user_router # noqa +from .achievement import router as achievement_router # noqa +from .course import router as course_router # noqa +from .examination import router as examination_router # noqa from .group import router as group_router # noqa +from .notification import router as notification_router # noqa from .profile import router as profile_router # noqa from .tariff import router as tariff_router # noqa -from .notification import router as notification_router # noqa -from .examination import router as examination_router # noqa -from .achievement import router as achievement_router # noqa -from .course import router as course_router # noqa from .task import router as task_router # noqa +from .user import router as user_router # noqa diff --git a/app/api/endpoints/achievement.py b/app/api/endpoints/achievement.py index b85b30a..915cb1b 100644 --- a/app/api/endpoints/achievement.py +++ b/app/api/endpoints/achievement.py @@ -2,38 +2,36 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.api.validators import check_name_duplicate -from app.schemas.achievement import AchievementRead, AchievementCreate from app.core.db import get_async_session from app.crud import achievement_crud +from app.schemas.achievement import AchievementCreate, AchievementRead from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=list[AchievementRead]) +@router.get("/", response_model=list[AchievementRead]) async def get_all_achievements( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session), ) -> list[AchievementRead]: """Возвращает все achievement.""" return await achievement_crud.get_multi(session) -@router.post('/', response_model=AchievementRead) +@router.post("/", response_model=AchievementRead) async def create_achievement( - achievement: AchievementCreate, - session: AsyncSession = Depends(get_async_session) + achievement: AchievementCreate, + session: AsyncSession = Depends(get_async_session) ): """Создать Achievement""" await check_name_duplicate(achievement.name, achievement_crud, session) - return await achievement_crud.create( - obj_in=achievement, session=session - ) + return await achievement_crud.create(obj_in=achievement, session=session) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_achievement( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" return await delete_obj( diff --git a/app/api/endpoints/course.py b/app/api/endpoints/course.py index ef3cd88..4aa7434 100644 --- a/app/api/endpoints/course.py +++ b/app/api/endpoints/course.py @@ -2,38 +2,35 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.api.validators import check_name_duplicate -from app.schemas.course import CourseCreate, CourseRead from app.core.db import get_async_session from app.crud import course_crud +from app.schemas.course import CourseCreate, CourseRead from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=list[CourseRead]) +@router.get("/", response_model=list[CourseRead]) async def get_all_courses( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session), ) -> list[CourseRead]: """Возвращает все courses.""" return await course_crud.get_multi(session) -@router.post('/', response_model=CourseRead) +@router.post("/", response_model=CourseRead) async def create_course( - course: CourseCreate, - session: AsyncSession = Depends(get_async_session) + course: CourseCreate, session: AsyncSession = Depends(get_async_session) ): """Создать Course""" await check_name_duplicate(course.name, course_crud, session) - return await course_crud.create( - obj_in=course, session=session - ) + return await course_crud.create(obj_in=course, session=session) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_course( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" return await delete_obj(obj_id=obj_id, crud=course_crud, session=session) diff --git a/app/api/endpoints/examination.py b/app/api/endpoints/examination.py index 0d2100e..6f55505 100644 --- a/app/api/endpoints/examination.py +++ b/app/api/endpoints/examination.py @@ -2,26 +2,26 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.api.validators import check_name_duplicate -from app.schemas.examination import ExaminationRead, ExaminationCreate from app.core.db import get_async_session from app.crud import examination_crud +from app.schemas.examination import ExaminationCreate, ExaminationRead from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=list[ExaminationRead]) +@router.get("/", response_model=list[ExaminationRead]) async def get_all_examinations( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session), ) -> list[ExaminationRead]: """Возвращает все Examination.""" return await examination_crud.get_multi(session) -@router.post('/', response_model=ExaminationRead) +@router.post("/", response_model=ExaminationRead) async def create_examination( - examination: ExaminationCreate, - session: AsyncSession = Depends(get_async_session) + examination: ExaminationCreate, + session: AsyncSession = Depends(get_async_session) ): """Создать Examination""" await check_name_duplicate(examination.name, examination_crud, session) @@ -30,10 +30,10 @@ async def create_examination( ) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_examination( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" return await delete_obj( diff --git a/app/api/endpoints/group.py b/app/api/endpoints/group.py index d23f740..778e9d4 100644 --- a/app/api/endpoints/group.py +++ b/app/api/endpoints/group.py @@ -1,39 +1,36 @@ from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession -from app.schemas.group import GroupRead, GroupCreate +from app.api.validators import check_name_duplicate from app.core.db import get_async_session from app.crud import group_crud -from app.api.validators import check_name_duplicate +from app.schemas.group import GroupCreate, GroupRead from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=list[GroupRead]) +@router.get("/", response_model=list[GroupRead]) async def get_all_groups( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session), ) -> list[GroupRead]: """Возвращает все группы.""" return await group_crud.get_multi(session) -@router.post('/', response_model=GroupRead) +@router.post("/", response_model=GroupRead) async def create_group( - group: GroupCreate, - session: AsyncSession = Depends(get_async_session) + group: GroupCreate, session: AsyncSession = Depends(get_async_session) ): """Создать группу""" await check_name_duplicate(group.name, group_crud, session) - return await group_crud.create( - obj_in=group, session=session - ) + return await group_crud.create(obj_in=group, session=session) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_group( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" return await delete_obj(obj_id=obj_id, crud=group_crud, session=session) diff --git a/app/api/endpoints/notification.py b/app/api/endpoints/notification.py index 1b7af1b..30cdbca 100644 --- a/app/api/endpoints/notification.py +++ b/app/api/endpoints/notification.py @@ -5,20 +5,19 @@ from app.crud import notification_crud from app.schemas.notification import NotificationCreate, NotificationRead - router = APIRouter() -@router.post('/') +@router.post("/") async def create_notification( notification: NotificationCreate, - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session) ): return await notification_crud.create(notification, session) -@router.get('/', response_model=list[NotificationRead]) +@router.get("/", response_model=list[NotificationRead]) async def get_all_notifications( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session) ): return await notification_crud.get_multi(session) diff --git a/app/api/endpoints/profile.py b/app/api/endpoints/profile.py index f387bf9..0d7dd14 100644 --- a/app/api/endpoints/profile.py +++ b/app/api/endpoints/profile.py @@ -2,31 +2,28 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.api.validators import check_obj_exists -from app.models import User -from app.schemas.profile import ProfileRead, ProfileCreate from app.core.db import get_async_session -from app.crud import profile_crud, user_crud from app.core.user import current_user +from app.crud import profile_crud, user_crud +from app.models import User +from app.schemas.profile import ProfileCreate, ProfileRead from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=list[ProfileRead]) +@router.get("/", response_model=list[ProfileRead]) async def get_all_profiles( - session: AsyncSession = Depends(get_async_session), - user: User = Depends(current_user) + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_user), ) -> list[ProfileRead]: """Возвращает все profile юзера.""" - return await profile_crud.get_users_obj( - user_id=user.id, session=session - ) + return await profile_crud.get_users_obj(user_id=user.id, session=session) -@router.post('/', response_model=ProfileRead) +@router.post("/", response_model=ProfileRead) async def create_profile( - profile: ProfileCreate, - session: AsyncSession = Depends(get_async_session) + profile: ProfileCreate, session: AsyncSession = Depends(get_async_session) ): """Создать Profile""" await check_obj_exists( @@ -37,10 +34,12 @@ async def create_profile( ) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_profile( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" - return await delete_obj(obj_id=obj_id, crud=profile_crud, session=session) + return await delete_obj( + obj_id=obj_id, crud=profile_crud, session=session + ) diff --git a/app/api/endpoints/tariff.py b/app/api/endpoints/tariff.py index feb7034..9cead6e 100644 --- a/app/api/endpoints/tariff.py +++ b/app/api/endpoints/tariff.py @@ -1,56 +1,53 @@ from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession +from app.api.validators import check_name_duplicate, check_obj_exists +from app.core.db import get_async_session from app.core.user import current_user +from app.crud import tariff_crud, user_crud from app.models import User -from app.schemas.tariff import TariffRead, TariffCreate +from app.schemas.tariff import TariffCreate, TariffRead from app.schemas.user import UserRead -from app.core.db import get_async_session -from app.crud import tariff_crud, user_crud -from app.api.validators import check_name_duplicate, check_obj_exists from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=list[TariffRead]) +@router.get("/", response_model=list[TariffRead]) async def get_all_tariffs( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session), ) -> list[TariffRead]: """Возвращает все тарифы.""" return await tariff_crud.get_multi(session) -@router.put('/', response_model=UserRead) +@router.put("/", response_model=UserRead) async def update_users_tariff( - tariff_id: int, - user: User = Depends(current_user), - session: AsyncSession = Depends(get_async_session) + tariff_id: int, + user: User = Depends(current_user), + session: AsyncSession = Depends(get_async_session), ) -> UserRead: """Привязывает тариф к юзеру""" await check_obj_exists(tariff_id, tariff_crud, session) user = await user_crud.update_id( - session=session, db_obj=user, field='tariff_id', field_value=tariff_id + session=session, db_obj=user, field="tariff_id", field_value=tariff_id ) return user -@router.post('/', response_model=TariffRead) +@router.post("/", response_model=TariffRead) async def create_tariff( - tariff: TariffCreate, - session: AsyncSession = Depends(get_async_session) + tariff: TariffCreate, session: AsyncSession = Depends(get_async_session) ): """Создать Тариф""" await check_name_duplicate(tariff.name, tariff_crud, session) - return await tariff_crud.create( - obj_in=tariff, session=session - ) + return await tariff_crud.create(obj_in=tariff, session=session) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_tariff( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" return await delete_obj(obj_id=obj_id, crud=tariff_crud, session=session) diff --git a/app/api/endpoints/task.py b/app/api/endpoints/task.py index 46ff0e5..a6f5bab 100644 --- a/app/api/endpoints/task.py +++ b/app/api/endpoints/task.py @@ -3,39 +3,36 @@ from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession -from app.schemas.task import TaskRead, TaskCreate +from app.api.validators import check_name_duplicate from app.core.db import get_async_session from app.crud import task_crud -from app.api.validators import check_name_duplicate +from app.schemas.task import TaskCreate, TaskRead from app.services.endpoints_services import delete_obj router = APIRouter() -@router.get('/', response_model=List[TaskRead]) +@router.get("/", response_model=List[TaskRead]) async def get_all_tasks( - session: AsyncSession = Depends(get_async_session) + session: AsyncSession = Depends(get_async_session), ) -> List[TaskRead]: """Возвращает все таски.""" return await task_crud.get_multi(session) -@router.post('/', response_model=TaskRead) +@router.post("/", response_model=TaskRead) async def create_task( - task: TaskCreate, - session: AsyncSession = Depends(get_async_session) + task: TaskCreate, session: AsyncSession = Depends(get_async_session) ): """Создать таску""" await check_name_duplicate(task.name, task_crud, session) - return await task_crud.create( - obj_in=task, session=session - ) + return await task_crud.create(obj_in=task, session=session) -@router.delete('/{obj_id}') +@router.delete("/{obj_id}") async def delete_task( - obj_id: int, - session: AsyncSession = Depends(get_async_session), + obj_id: int, + session: AsyncSession = Depends(get_async_session), ): """Удалить объект""" return await delete_obj(obj_id=obj_id, crud=task_crud, session=session) diff --git a/app/api/routers.py b/app/api/routers.py index 729db24..4c6d82f 100644 --- a/app/api/routers.py +++ b/app/api/routers.py @@ -1,32 +1,23 @@ from fastapi import APIRouter -from app.api.endpoints import ( - user_router, group_router, tariff_router, - examination_router, course_router, task_router, - achievement_router, profile_router, -) +from app.api.endpoints import (achievement_router, course_router, + examination_router, group_router, + profile_router, tariff_router, task_router, + user_router) main_router = APIRouter() main_router.include_router(user_router) +main_router.include_router(group_router, prefix="/groups", tags=["Groups"]) +main_router.include_router(tariff_router, prefix="/tariffs", tags=["Tariffs"]) main_router.include_router( - group_router, prefix='/groups', tags=['Groups'] -) -main_router.include_router( - tariff_router, prefix='/tariffs', tags=['Tariffs'] -) -main_router.include_router( - examination_router, prefix='/examinations', tags=['Examinations'] -) -main_router.include_router( - course_router, prefix='/courses', tags=['Courses'] -) -main_router.include_router( - task_router, prefix='/tasks', tags=['Tasks'] + examination_router, prefix="/examinations", tags=["Examinations"] ) +main_router.include_router(course_router, prefix="/courses", tags=["Courses"]) +main_router.include_router(task_router, prefix="/tasks", tags=["Tasks"]) main_router.include_router( - achievement_router, prefix='/achievements', tags=['Achievements'] + achievement_router, prefix="/achievements", tags=["Achievements"] ) main_router.include_router( - profile_router, prefix='/profiles', tags=['Profiles'] + profile_router, prefix="/profiles", tags=["Profiles"] ) diff --git a/app/api/validators.py b/app/api/validators.py index aec600c..4296e62 100644 --- a/app/api/validators.py +++ b/app/api/validators.py @@ -13,8 +13,8 @@ async def check_obj_exists( if obj is None: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, - detail=f"Объект {crud.model.__tablename__}" - f" с id {obj_id} не найден." + detail=f"Объект {crud.model.__tablename__} " + f"f с id {obj_id} не найден.", ) @@ -23,12 +23,10 @@ async def check_name_duplicate( crud, session: AsyncSession, ) -> None: - obj = await crud.get_obj_by_name( - name, session - ) + obj = await crud.get_obj_by_name(name, session) if obj is not None: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=f"Объект {crud.model.__tablename__}" - f" с таким именем уже существует!", + f" с таким именем уже существует!", ) diff --git a/app/core/base.py b/app/core/base.py index 0b1ac91..6b464d0 100644 --- a/app/core/base.py +++ b/app/core/base.py @@ -1,4 +1,2 @@ from app.core.db import Base # noqa -from app.models import ( # noqa - Group, User, Profile, Tariff -) +from app.models import Group, Profile, Tariff, User # noqa diff --git a/app/core/config.py b/app/core/config.py index 69ad20d..f98c3fb 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,4 +1,5 @@ from typing import Optional + from pydantic_settings import BaseSettings diff --git a/app/core/init_db.py b/app/core/init_db.py index d07966a..d3faec3 100644 --- a/app/core/init_db.py +++ b/app/core/init_db.py @@ -22,10 +22,11 @@ async def create_user( async with get_user_manager_context(user_db) as user_manager: await user_manager.create( UserCreate( - username='admin', - email=email, password=password, + username="admin", + email=email, + password=password, is_superuser=is_superuser, - role='admin', + role="admin", ) ) @@ -35,8 +36,8 @@ async def create_user( async def create_first_superuser(): if ( - settings.first_superuser_email is not None and - settings.first_superuser_password is not None + settings.first_superuser_email is not None + and settings.first_superuser_password is not None ): await create_user( email=settings.first_superuser_email, diff --git a/app/core/user.py b/app/core/user.py index f53fa80..98e37b7 100644 --- a/app/core/user.py +++ b/app/core/user.py @@ -2,17 +2,10 @@ from typing import Optional, Union from fastapi import Depends, Request -from fastapi_users import ( - BaseUserManager, - FastAPIUsers, - IntegerIDMixin, - InvalidPasswordException, -) -from fastapi_users.authentication import ( - AuthenticationBackend, - BearerTransport, - JWTStrategy, -) +from fastapi_users import (BaseUserManager, FastAPIUsers, IntegerIDMixin, + InvalidPasswordException) +from fastapi_users.authentication import (AuthenticationBackend, + BearerTransport, JWTStrategy) from fastapi_users_db_sqlalchemy import SQLAlchemyUserDatabase from sqlalchemy.ext.asyncio import AsyncSession diff --git a/app/crud/__init__.py b/app/crud/__init__.py index ab6b702..527ec83 100644 --- a/app/crud/__init__.py +++ b/app/crud/__init__.py @@ -1,9 +1,9 @@ +from .achievement import achievement_crud # noqa +from .course import course_crud # noqa +from .examination import examination_crud # noqa from .group import group_crud # noqa -from .user import user_crud # noqa +from .notification import notification_crud # noqa from .profile import profile_crud # noqa from .tariff import tariff_crud # noqa -from .notification import notification_crud # noqa -from .examination import examination_crud # noqa -from .achievement import achievement_crud # noqa -from .course import course_crud # noqa from .task import task_crud # noqa +from .user import user_crud # noqa diff --git a/app/crud/base.py b/app/crud/base.py index a462733..c5b9082 100644 --- a/app/crud/base.py +++ b/app/crud/base.py @@ -26,9 +26,9 @@ async def get_multi(self, session: AsyncSession): return db_objs.scalars().all() async def get_obj_by_name( - self, - name: str, - session: AsyncSession, + self, + name: str, + session: AsyncSession, ): db_obj = await session.execute( select(self.model).where(self.model.name == name) diff --git a/app/crud/profile.py b/app/crud/profile.py index 4539cc2..387a072 100644 --- a/app/crud/profile.py +++ b/app/crud/profile.py @@ -6,13 +6,11 @@ class CRUDProfile(CRUDBase): - async def create( - self, obj_in: ProfileCreate, - user_id: int, session: AsyncSession + self, obj_in: ProfileCreate, user_id: int, session: AsyncSession ): obj_in_data: dict = obj_in.model_dump() - obj_in_data['user_id'] = user_id + obj_in_data["user_id"] = user_id db_obj = Profile(**obj_in_data) session.add(db_obj) await session.commit() diff --git a/app/models/__init__.py b/app/models/__init__.py index a4683bd..59b9338 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -1,9 +1,9 @@ -from .user import User # noqa +from .achievement import Achievement # noqa +from .course import Course # noqa +from .examination import Examination # noqa from .group import Group # noqa +from .notification import Notification # noqa from .profile import Profile # noqa from .tariff import Tariff # noqa -from .notification import Notification # noqa -from .examination import Examination # noqa -from .achievement import Achievement # noqa -from .course import Course # noqa from .task import Task # noqa +from .user import User # noqa diff --git a/app/models/achievement.py b/app/models/achievement.py index f9a0461..3881702 100644 --- a/app/models/achievement.py +++ b/app/models/achievement.py @@ -2,32 +2,36 @@ from typing import TYPE_CHECKING -from sqlalchemy import (Column, String, Text, Table, ForeignKey, Integer, +from sqlalchemy import (Column, ForeignKey, Integer, String, Table, Text, UniqueConstraint) from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base if TYPE_CHECKING: from app.models import Profile achievement_profile_association = Table( - 'achievement_profile_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('achievement_id', ForeignKey('achievement.id')), - Column('profile_id', ForeignKey('profile.id')), - UniqueConstraint('achievement_id', 'profile_id', - name='constraint_achievement_profile') + "achievement_profile_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("achievement_id", ForeignKey("achievement.id")), + Column("profile_id", ForeignKey("profile.id")), + UniqueConstraint( + "achievement_id", "profile_id", + name="constraint_achievement_profile" + ), ) class Achievement(Base): - name: str = Column(String(length=settings.max_length_string), unique=True, - nullable=False) + name: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: str = Column(Text) profiles: Mapped[list[Profile]] = relationship( secondary=achievement_profile_association, - back_populates='achievements' + back_populates="achievements" ) diff --git a/app/models/course.py b/app/models/course.py index d2c0615..d951f6e 100644 --- a/app/models/course.py +++ b/app/models/course.py @@ -2,53 +2,53 @@ from typing import TYPE_CHECKING -from sqlalchemy import (Column, String, Text, Table, ForeignKey, Integer, +from sqlalchemy import (Column, ForeignKey, Integer, String, Table, Text, UniqueConstraint) from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base from .task import task_course_association if TYPE_CHECKING: - from .user import User from .tariff import Tariff from .task import Task + from .user import User course_user_association = Table( - 'course_user_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('course_id', ForeignKey('course.id')), - Column('user_id', ForeignKey('user.id')), - UniqueConstraint('course_id', 'user_id', - name='constraint_course_user') + "course_user_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("course_id", ForeignKey("course.id")), + Column("user_id", ForeignKey("user.id")), + UniqueConstraint("course_id", "user_id", name="constraint_course_user"), ) course_tariff_association = Table( - 'course_tariff_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('course_id', ForeignKey('course.id')), - Column('tariff_id', ForeignKey('tariff.id')), - UniqueConstraint('course_id', 'tariff_id', - name='constraint_course_tariff') + "course_tariff_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("course_id", ForeignKey("course.id")), + Column("tariff_id", ForeignKey("tariff.id")), + UniqueConstraint( + "course_id", "tariff_id", name="constraint_course_tariff" + ), ) class Course(Base): - name: str = Column(String(length=settings.max_length_string), unique=True, - nullable=False) + name: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: str = Column(Text) users: Mapped[list[User]] = relationship( - secondary=course_user_association, - back_populates='courses' + secondary=course_user_association, back_populates="courses" ) tariffs: Mapped[list[Tariff]] = relationship( - secondary=course_tariff_association, - back_populates='courses' + secondary=course_tariff_association, back_populates="courses" ) tasks: Mapped[list[Task]] = relationship( - secondary=task_course_association, - back_populates='courses' + secondary=task_course_association, back_populates="courses" ) diff --git a/app/models/examination.py b/app/models/examination.py index c1406cf..87a82d4 100644 --- a/app/models/examination.py +++ b/app/models/examination.py @@ -2,32 +2,34 @@ from typing import TYPE_CHECKING -from sqlalchemy import (Column, String, Text, Table, ForeignKey, Integer, +from sqlalchemy import (Column, ForeignKey, Integer, String, Table, Text, UniqueConstraint) from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base if TYPE_CHECKING: from app.models import User examination_user_association = Table( - 'examination_user_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('examination_id', ForeignKey('examination.id')), - Column('user_id', ForeignKey('user.id')), - UniqueConstraint('examination_id', 'user_id', - name='constraint_examination_user') + "examination_user_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("examination_id", ForeignKey("examination.id")), + Column("user_id", ForeignKey("user.id")), + UniqueConstraint( + "examination_id", "user_id", name="constraint_examination_user" + ), ) class Examination(Base): - name: str = Column(String(length=settings.max_length_string), unique=True, - nullable=False) + name: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: str = Column(Text) users: Mapped[list[User]] = relationship( - secondary=examination_user_association, - back_populates='examinations' + secondary=examination_user_association, back_populates="examinations" ) diff --git a/app/models/group.py b/app/models/group.py index d817489..da59b0b 100644 --- a/app/models/group.py +++ b/app/models/group.py @@ -1,30 +1,33 @@ from __future__ import annotations -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING, List -from sqlalchemy import (Column, String, Text, Table, ForeignKey, Integer, +from sqlalchemy import (Column, ForeignKey, Integer, String, Table, Text, UniqueConstraint) from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base if TYPE_CHECKING: from app.models import User group_user_association = Table( - 'group_user_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('group_id', ForeignKey('group.id')), - Column('user_id', ForeignKey('user.id')), - UniqueConstraint('group_id', 'user_id', name='constraint_group_user') + "group_user_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("group_id", ForeignKey("group.id")), + Column("user_id", ForeignKey("user.id")), + UniqueConstraint("group_id", "user_id", name="constraint_group_user"), ) class Group(Base): - name: str = Column(String(length=settings.max_length_string), unique=True, - nullable=False) + name: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: str = Column(Text) - users: Mapped[List[User]] = relationship(secondary=group_user_association, - back_populates='groups') + users: Mapped[List[User]] = relationship( + secondary=group_user_association, back_populates="groups" + ) diff --git a/app/models/notification.py b/app/models/notification.py index ac56076..8d145c8 100644 --- a/app/models/notification.py +++ b/app/models/notification.py @@ -2,32 +2,34 @@ from typing import TYPE_CHECKING -from sqlalchemy import (Column, String, Text, Table, ForeignKey, Integer, +from sqlalchemy import (Column, ForeignKey, Integer, String, Table, Text, UniqueConstraint) from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base if TYPE_CHECKING: from app.models import User notification_user_association = Table( - 'notification_user_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('notification_id', ForeignKey('notification.id')), - Column('user_id', ForeignKey('user.id')), - UniqueConstraint('notification_id', 'user_id', - name='constraint_notification_user') + "notification_user_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("notification_id", ForeignKey("notification.id")), + Column("user_id", ForeignKey("user.id")), + UniqueConstraint( + "notification_id", "user_id", name="constraint_notification_user" + ), ) class Notification(Base): - name: str = Column(String(length=settings.max_length_string), unique=True, - nullable=False) + name: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: str = Column(Text) users: Mapped[list[User]] = relationship( - secondary=notification_user_association, - back_populates='notifications' + secondary=notification_user_association, back_populates="notifications" ) diff --git a/app/models/profile.py b/app/models/profile.py index 6f57822..90b4e1d 100644 --- a/app/models/profile.py +++ b/app/models/profile.py @@ -2,27 +2,25 @@ from typing import TYPE_CHECKING -from sqlalchemy import Column, String, SmallInteger, ForeignKey +from sqlalchemy import Column, ForeignKey, SmallInteger, String from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base + from .achievement import achievement_profile_association if TYPE_CHECKING: - from .user import User from .achievement import Achievement + from .user import User class Profile(Base): first_name: Mapped[str] = Column(String(length=settings.max_length_string)) last_name: Mapped[str] = Column(String(length=settings.max_length_string)) age: Mapped[int] = Column(SmallInteger) - user_id: Mapped[int] = Column( - ForeignKey('user.id'), unique=True - ) - user: Mapped[User] = relationship(back_populates='profile') + user_id: Mapped[int] = Column(ForeignKey("user.id"), unique=True) + user: Mapped[User] = relationship(back_populates="profile") achievements: Mapped[Achievement] = relationship( - secondary=achievement_profile_association, - back_populates='profiles' + secondary=achievement_profile_association, back_populates="profiles" ) diff --git a/app/models/tariff.py b/app/models/tariff.py index 3a2e7fd..0e0ed18 100644 --- a/app/models/tariff.py +++ b/app/models/tariff.py @@ -5,21 +5,22 @@ from sqlalchemy import Column, String, Text from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base + from .course import course_tariff_association if TYPE_CHECKING: - from .user import User from .course import Course + from .user import User class Tariff(Base): - name: Mapped[str] = Column(String(length=settings.max_length_string), - unique=True, nullable=False) + name: Mapped[str] = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: Mapped[str] = Column(Text) - users: Mapped[list[User]] = relationship(back_populates='tariff') + users: Mapped[list[User]] = relationship(back_populates="tariff") courses: Mapped[list[Course]] = relationship( - secondary=course_tariff_association, - back_populates='tariffs' + secondary=course_tariff_association, back_populates="tariffs" ) diff --git a/app/models/task.py b/app/models/task.py index 930fd90..dd20d45 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -2,32 +2,32 @@ from typing import TYPE_CHECKING -from sqlalchemy import (Column, String, Text, Table, ForeignKey, Integer, +from sqlalchemy import (Column, ForeignKey, Integer, String, Table, Text, UniqueConstraint) from sqlalchemy.orm import Mapped, relationship -from app.core.db import Base from app.core.config import settings +from app.core.db import Base if TYPE_CHECKING: from .user import Course task_course_association = Table( - 'task_course_association', Base.metadata, - Column('id', Integer, primary_key=True), - Column('task_id', ForeignKey('task.id')), - Column('course_id', ForeignKey('course.id')), - UniqueConstraint('task_id', 'course_id', - name='constraint_task_course') + "task_course_association", + Base.metadata, + Column("id", Integer, primary_key=True), + Column("task_id", ForeignKey("task.id")), + Column("course_id", ForeignKey("course.id")), + UniqueConstraint("task_id", "course_id", name="constraint_task_course"), ) class Task(Base): - name: str = Column(String(length=settings.max_length_string), unique=True, - nullable=False) + name: str = Column( + String(length=settings.max_length_string), unique=True, nullable=False + ) description: str = Column(Text) courses: Mapped[list[Course]] = relationship( - secondary=task_course_association, - back_populates='tasks' + secondary=task_course_association, back_populates="tasks" ) diff --git a/app/models/user.py b/app/models/user.py index c2fe8f6..64589d7 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -4,28 +4,29 @@ from typing import TYPE_CHECKING from fastapi_users_db_sqlalchemy import SQLAlchemyBaseUserTable -from sqlalchemy import Enum, Column, String, ForeignKey +from sqlalchemy import Column, Enum, ForeignKey, String from sqlalchemy.orm import Mapped, relationship from app.core.db import Base + +from .course import course_user_association +from .examination import examination_user_association from .group import group_user_association from .notification import notification_user_association -from .examination import examination_user_association -from .course import course_user_association if TYPE_CHECKING: - from .tariff import Tariff - from .profile import Profile + from .course import Course + from .examination import Examination from .group import Group from .notification import Notification - from .examination import Examination - from .course import Course + from .profile import Profile + from .tariff import Tariff class UserRoleEnum(enum.Enum): - user = 'user' - manager = 'manager' - admin = 'admin' + user = "user" + manager = "manager" + admin = "admin" class User(SQLAlchemyBaseUserTable[int], Base): @@ -34,23 +35,19 @@ class User(SQLAlchemyBaseUserTable[int], Base): ) username: Mapped[str] = Column(String(length=100), nullable=False) tariff_id: Mapped[int] = Column( - ForeignKey('tariff.id'), + ForeignKey("tariff.id"), ) - tariff: Mapped[Tariff] = relationship(back_populates='users') - profile: Mapped[Profile] = relationship(back_populates='user') + tariff: Mapped[Tariff] = relationship(back_populates="users") + profile: Mapped[Profile] = relationship(back_populates="user") groups: Mapped[Group] = relationship( - secondary=group_user_association, - back_populates='users' + secondary=group_user_association, back_populates="users" ) notifications: Mapped[Notification] = relationship( - secondary=notification_user_association, - back_populates='users' + secondary=notification_user_association, back_populates="users" ) examinations: Mapped[Examination] = relationship( - secondary=examination_user_association, - back_populates='users' + secondary=examination_user_association, back_populates="users" ) courses: Mapped[Course] = relationship( - secondary=course_user_association, - back_populates='users' + secondary=course_user_association, back_populates="users" ) diff --git a/app/requirements.txt b/app/requirements.txt index b58a8df..eb33647 100644 Binary files a/app/requirements.txt and b/app/requirements.txt differ diff --git a/app/schemas/user.py b/app/schemas/user.py index bba2b33..f635080 100644 --- a/app/schemas/user.py +++ b/app/schemas/user.py @@ -5,9 +5,9 @@ class Role(str, Enum): - user = 'user' - manager = 'manager' - admin = 'admin' + user = "user" + manager = "manager" + admin = "admin" class UserRead(schemas.BaseUser[int]): diff --git a/app/services/endpoints_services.py b/app/services/endpoints_services.py index e2eb38d..e47d75f 100644 --- a/app/services/endpoints_services.py +++ b/app/services/endpoints_services.py @@ -16,6 +16,6 @@ async def delete_obj( except UnmappedInstanceError: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, - detail=f'Объект {crud.model.__tablename__}' - f' с id {obj_id} не найден.' + detail=f"Объект {crud.model.__tablename__} " + f"с id {obj_id} не найден.", ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bede0a3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +# isort +[tool.isort] +skip=["alembic", "venv"] + +[tool.flake8] +exclude=["alembic", "venv"] \ No newline at end of file