diff --git a/backend/dependecies.py b/backend/dependecies.py index d76e634..35bdf47 100644 --- a/backend/dependecies.py +++ b/backend/dependecies.py @@ -4,8 +4,10 @@ from fastapi import Depends from sqlalchemy.ext.asyncio import AsyncSession from backend.session import get_session -from schemas.base import PaginationSchema +from schemas.base import PaginationSchema, SortingSchema from utils.pagination import pagination_parameters +from utils.sorting import sorting_parameters SessionDependency = Annotated[AsyncSession, Depends(get_session)] PaginationDependency = Annotated[PaginationSchema, Depends(pagination_parameters)] +SortingDependency = Annotated[SortingSchema, Depends(sorting_parameters)] diff --git a/repositories/deal.py b/repositories/deal.py index 51c4aa4..5ea36ac 100644 --- a/repositories/deal.py +++ b/repositories/deal.py @@ -4,27 +4,43 @@ from sqlalchemy import select from models import Deal, CardStatusHistory, Board from repositories.base import BaseRepository +from schemas.base import SortDir from schemas.deal import UpdateDealSchema, CreateDealSchema +from utils.sorting import apply_sorting class DealRepository(BaseRepository): async def get_all( self, - board_id: Optional[int], - project_id: Optional[int], page: Optional[int], items_per_page: Optional[int], + field: Optional[str], + direction: Optional[SortDir], + project_id: Optional[int], + board_id: Optional[int], + status_id: Optional[int], + id: Optional[int], + name: Optional[str], ) -> tuple[list[Deal], int]: stmt = select(Deal).where(Deal.is_deleted.is_(False)) - if board_id: - stmt = stmt.where(Deal.board_id == board_id) + if id: + stmt = stmt.where(Deal.id == id) if project_id: stmt = stmt.join(Board).where(Board.project_id == project_id) + if board_id: + stmt = stmt.where(Deal.board_id == board_id) + if status_id: + stmt = stmt.where(Deal.status_id == status_id) + if name: + stmt = stmt.where(Deal.name.ilike(f"%{name}%")) total_items = len((await self.session.execute(stmt)).all()) - stmt = stmt.order_by(Deal.lexorank) + if field and direction is not None: + stmt = apply_sorting(stmt, Deal, field, direction) + else: + stmt = stmt.order_by(Deal.lexorank) if page and items_per_page: stmt = self._apply_pagination(stmt, page, items_per_page) diff --git a/routers/deal.py b/routers/deal.py index 256abe9..2ac2d60 100644 --- a/routers/deal.py +++ b/routers/deal.py @@ -1,6 +1,10 @@ from fastapi import APIRouter, Path, Query -from backend.dependecies import SessionDependency, PaginationDependency +from backend.dependecies import ( + SessionDependency, + PaginationDependency, + SortingDependency, +) from schemas.deal import * from services import DealService @@ -17,10 +21,22 @@ deal_router = APIRouter( async def get_deals( session: SessionDependency, pagination: PaginationDependency, - board_id: Optional[int] = Query(alias="boardId", default=None), + sorting: SortingDependency, project_id: Optional[int] = Query(alias="projectId", default=None), + board_id: Optional[int] = Query(alias="boardId", default=None), + status_id: Optional[int] = Query(alias="statusId", default=None), + id: Optional[int] = Query(default=None), + name: Optional[str] = Query(default=None), ): - return await DealService(session).get_deals(pagination, board_id, project_id) + return await DealService(session).get_deals( + pagination, + sorting, + project_id, + board_id, + status_id, + id, + name, + ) @deal_router.post( diff --git a/schemas/base.py b/schemas/base.py index ddc723b..49b797a 100644 --- a/schemas/base.py +++ b/schemas/base.py @@ -1,4 +1,4 @@ -from typing import Self, Optional +from typing import Self, Optional, Literal from pydantic import BaseModel from pydantic.alias_generators import to_camel @@ -45,6 +45,14 @@ class PaginationInfoSchema(BaseSchema): total_items: int +type SortDir = Literal["asc", "desc"] + + +class SortingSchema(BaseSchema): + field: Optional[str] = None + direction: Optional[SortDir] = None + + class BaseEnumSchema(BaseSchema): id: int name: str diff --git a/schemas/deal.py b/schemas/deal.py index e5837ba..c79d253 100644 --- a/schemas/deal.py +++ b/schemas/deal.py @@ -12,6 +12,7 @@ class DealSchema(BaseSchema): name: str lexorank: str status_id: int + board_id: int created_at: datetime diff --git a/services/deal.py b/services/deal.py index 1ddbaac..479d20e 100644 --- a/services/deal.py +++ b/services/deal.py @@ -4,7 +4,7 @@ from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from repositories import DealRepository -from schemas.base import PaginationSchema +from schemas.base import PaginationSchema, SortingSchema from schemas.deal import * @@ -15,11 +15,15 @@ class DealService: async def get_deals( self, pagination: PaginationSchema, - board_id: Optional[int], - project_id: Optional[int], + sorting: SortingSchema, + *filters, ) -> GetDealsResponse: deals, total_items = await self.repository.get_all( - board_id, project_id, pagination.page, pagination.items_per_page + pagination.page, + pagination.items_per_page, + sorting.field, + sorting.direction, + *filters, ) total_pages = 1 diff --git a/utils/sorting.py b/utils/sorting.py new file mode 100644 index 0000000..01c5076 --- /dev/null +++ b/utils/sorting.py @@ -0,0 +1,21 @@ +from typing import Optional + +from fastapi import Query +from sqlalchemy import Select + +from schemas.base import SortingSchema, SortDir +from utils.strings import camel_to_snake + + +async def sorting_parameters( + field: Optional[str] = Query(default=None, alias="sortingField"), + direction: Optional[SortDir] = Query(default=None, alias="sortingDirection"), +) -> SortingSchema: + return SortingSchema(field=camel_to_snake(field), direction=direction) + + +def apply_sorting(stmt: Select, cls: type, field: str, direction: SortDir) -> Select: + attr = getattr(cls, field) + if direction == "asc": + return stmt.order_by(attr.asc()) + return stmt.order_by(attr.desc()) diff --git a/utils/strings.py b/utils/strings.py new file mode 100644 index 0000000..51591fa --- /dev/null +++ b/utils/strings.py @@ -0,0 +1,8 @@ +import re +from typing import Optional + + +def camel_to_snake(string: Optional[str]) -> str: + if not string: + return string + return re.sub(r"(?