diff --git a/routers/card.py b/routers/card.py index 6c6a016..c9b0074 100644 --- a/routers/card.py +++ b/routers/card.py @@ -5,7 +5,7 @@ from typing import Annotated from fastapi import APIRouter, Depends, Response, UploadFile from sqlalchemy.ext.asyncio import AsyncSession -from backend.dependecies import SessionDependency, CurrentUserDependency +from backend.dependecies import SessionDependency, CurrentUserDependency, PaginationDependency from backend.session import get_session from generators.deal_pdf_generator.generator import DealTechSpecPdfGenerator from models import User @@ -78,7 +78,7 @@ async def change_status( return await CardsService(session).change_status_manual(request, user) -@card_router.get( +@card_router.post( '/summaries', response_model=CardSummaryResponse, operation_id='getCardSummaries', @@ -86,9 +86,10 @@ async def change_status( ) async def get_summary( session: Annotated[AsyncSession, Depends(get_session)], - full: Optional[bool] + request: GetCardSummariesRequest, + pagination: PaginationDependency, ): - return await CardsService(session).get_summary(full) + return await CardsService(session).get_summary(request, pagination) @card_router.post( diff --git a/schemas/card.py b/schemas/card.py index 569288d..35acd13 100644 --- a/schemas/card.py +++ b/schemas/card.py @@ -4,7 +4,7 @@ from typing import List, Optional, Union from pydantic import constr from schemas.attribute import CardAttributeSchema -from schemas.base import BaseSchema, OkMessageSchema +from schemas.base import BaseSchema, OkMessageSchema, PaginationInfoSchema from schemas.billing import CardBillRequestSchema from schemas.board import BoardSchema from schemas.card_tag import CardTagSchema @@ -164,6 +164,18 @@ class CardChangeStatusRequest(BaseSchema): new_status: int +class GetCardSummariesRequest(BaseSchema): + full: bool + card_id: Optional[int | str] = None + card_name: Optional[str] = None + marketplace_key: Optional[str] = None + shipping_warehouse_id: Optional[int] = None + client_id: Optional[int] = None + project_id: Optional[int] = None + board_id: Optional[int] = None + status_id: Optional[int] = None + + class CardCreateRequest(BaseSchema): name: str status_id: int @@ -345,6 +357,7 @@ class CardQuickCreateResponse(BaseSchema): class CardSummaryResponse(BaseSchema): summaries: List[CardSummary] + pagination_info: PaginationInfoSchema class CardAddServicesResponse(BaseSchema): diff --git a/services/card.py b/services/card.py index de36700..96edeec 100644 --- a/services/card.py +++ b/services/card.py @@ -1,14 +1,16 @@ from collections import defaultdict import lexorank +import math from fastapi import HTTPException -from sqlalchemy import select, func, update, delete, insert, and_ +from sqlalchemy import select, func, update, delete, insert, and_, Select from sqlalchemy.orm import joinedload, selectinload from starlette import status from card_attributes import CardAttributesCommandHandler from card_attributes.exceptions import CardAttributeException from models import * +from schemas.base import PaginationSchema from schemas.card import * from schemas.client import ClientDetailsSchema from services import card_group @@ -179,7 +181,61 @@ class CardsService(BaseService): .subquery() ) - async def get_summary(self, full: bool = False) -> CardSummaryResponse: + @staticmethod + def _apply_pagination(query: Select, pagination: PaginationSchema) -> Select: + offset = (pagination.page - 1) * pagination.items_per_page + query = ( + query + .offset(offset) + .limit(pagination.items_per_page) + ) + return query + + @staticmethod + def _apply_summary_filters(query: Select, request: GetCardSummariesRequest) -> Select: + if not request.full: + return query.where(Card.is_completed == False) + + if request.card_id: + query = query.where(Card.id == request.card_id) + if request.card_name: + query = query.where(Card.name.like(f"%{request.card_name}%")) + + if request.status_id: + query = query.where(Card.current_status_id == request.status_id) + elif request.board_id: + query = query.where(Card.board_id == request.board_id) + elif request.project_id: + query = ( + query + .join(Board) + .where(Board.project_id == request.project_id) + ) + + if request.client_id: + query = query.where(Card.client_id == request.client_id) + if request.marketplace_key: + query = query.where(Card.base_marketplace_key == request.marketplace_key) + if request.shipping_warehouse_id: + query = query.where(Card.shipping_warehouse_id == request.shipping_warehouse_id) + + query = query.order_by(Card.created_at.desc()) + + return query + + async def _summaries_pagination_info(self, query: Select, pagination: PaginationSchema) -> PaginationInfoSchema: + if not pagination.items_per_page: + return PaginationInfoSchema(total_pages=0, total_items=0) + + summaries = (await self.session.scalars(query)).all() + + total_items = len(summaries) + return PaginationInfoSchema( + total_pages=math.ceil(total_items / pagination.items_per_page), + total_items=total_items, + ) + + async def get_summary(self, request: GetCardSummariesRequest, pagination: PaginationSchema) -> CardSummaryResponse: price_subquery = self._get_price_subquery() products_quantity_subquery = self._get_products_quantity_subquery() q = ( @@ -210,13 +266,14 @@ class CardsService(BaseService): Card.is_deleted == False, ) ) - if not full: - q = q.where( - Card.is_completed == False, - # Card.current_status != CardStatus.COMPLETED - ) - else: - q = q.order_by(Card.created_at.desc()) + + q = self._apply_summary_filters(q, request) + + pagination_info = await self._summaries_pagination_info(q, pagination) + + if pagination.page and pagination.items_per_page: + q = CardsService._apply_pagination(q, pagination) + cards_query = await self.session.execute(q) summaries = [] for card, total_price, rank, products_count in cards_query.all(): @@ -247,7 +304,7 @@ class CardsService(BaseService): attributes=attributes, ) ) - return CardSummaryResponse(summaries=summaries) + return CardSummaryResponse(summaries=summaries, pagination_info=pagination_info) async def get_all(self) -> CardGetAllResponse: cards_stmt = (