diff --git a/src/application/abstractions/repositories/i_order_repository.py b/src/application/abstractions/repositories/i_order_repository.py index c8742e7..af174a7 100644 --- a/src/application/abstractions/repositories/i_order_repository.py +++ b/src/application/abstractions/repositories/i_order_repository.py @@ -27,7 +27,12 @@ class IOrderRepository(ABC): @abstractmethod - async def sum_by_date(self,*,year: int,month: int) -> Decimal: + async def sum_by_month(self,*,year: int,month: int, search: str | None = None) -> Decimal: + raise NotImplementedError + + + @abstractmethod + async def sum_by_day(self,*,year: int,month: int,day: int, search: str | None = None) -> Decimal: raise NotImplementedError @@ -37,10 +42,10 @@ class IOrderRepository(ABC): @abstractmethod - async def count_by_date(self,*,year: int,month: int | None = None) -> int: + async def count_by_month(self,*,year: int,month: int | None = None, search: str | None = None) -> int: raise NotImplementedError @abstractmethod - async def count_by_user_id(self,*,user_id: str) -> int: + async def count_by_day(self,*,year: int,month: int,day: int, search: str | None = None) -> int: raise NotImplementedError diff --git a/src/application/abstractions/repositories/i_purchase_request_repository.py b/src/application/abstractions/repositories/i_purchase_request_repository.py index a99acbb..aedca60 100644 --- a/src/application/abstractions/repositories/i_purchase_request_repository.py +++ b/src/application/abstractions/repositories/i_purchase_request_repository.py @@ -31,3 +31,26 @@ class IPurchaseRequestRepository(ABC): @abstractmethod async def count_all(self, *, status: str | None, organization_id: str | None) -> int: raise NotImplementedError + + @abstractmethod + async def count_for_month( + self, + *, + year: int, + month: int, + status: str | None, + organization_id: str | None + ) -> int: + raise NotImplementedError + + @abstractmethod + async def count_for_day( + self, + *, + year: int, + month: int, + day: int, + status: str | None, + organization_id: str | None + ) -> int: + raise NotImplementedError \ No newline at end of file diff --git a/src/application/commands/__init__.py b/src/application/commands/__init__.py index f15dccb..a051cda 100644 --- a/src/application/commands/__init__.py +++ b/src/application/commands/__init__.py @@ -44,6 +44,14 @@ from src.application.commands.orders_commands import ( ListOrdersByDateCommand, GetOrderCommand, ) +from src.application.commands.analytics_commands import ( + GetPurchaseAnalyticsForYearCommand, + GetOrdersAnalyticsForYearCommand, + GetPurchaseAnalyticsForMonthCommand, + GetOrdersAnalyticsForMonthCommand, + GetPurchaseAnalyticsForWeekCommand, + GetOrdersAnalyticsForWeekCommand, +) __all__ = [ 'AdminLoginCommand', @@ -78,4 +86,10 @@ __all__ = [ 'ListUserOrdersCommand', 'ListOrdersByDateCommand', 'GetOrderCommand', + 'GetPurchaseAnalyticsForYearCommand', + 'GetOrdersAnalyticsForYearCommand', + 'GetPurchaseAnalyticsForMonthCommand', + 'GetOrdersAnalyticsForMonthCommand', + 'GetPurchaseAnalyticsForWeekCommand', + 'GetOrdersAnalyticsForWeekCommand', ] diff --git a/src/application/commands/analytics_commands.py b/src/application/commands/analytics_commands.py new file mode 100644 index 0000000..40ac8f8 --- /dev/null +++ b/src/application/commands/analytics_commands.py @@ -0,0 +1,239 @@ +from __future__ import annotations +from dataclasses import dataclass +from decimal import Decimal +from datetime import date, timedelta + +from src.application.abstractions import IUnitOfWork +from src.application.contracts import ILogger +from src.application.domain.entities import OrderEntity, PurchaseRequestEntity +from src.application.domain.exceptions import NotFoundException +from src.infrastructure.database.decorators import transactional + + +YEAR=12 +MONTH=30 +WEEK=7 + +def _count_date( + year: int, + month: int | None = None, + day: int | None = None, +) -> tuple[int, int | None, int | None]: + if month is None: + return year - 1, None, None + + if day is None: + return (year - 1, 12, None) if month == 1 else (year, month - 1, None) + + prev_date = date(year, month, day) - timedelta(days=1) + return prev_date.year, prev_date.month, prev_date.day + + +class GetOrdersAnalyticsForYearCommand: + def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): + self._unit_of_work = unit_of_work + self._logger = logger + + + @transactional + async def __call__( + self, + *, + start_year: int, + start_month: int | None = None, + search: str | None = None, + ) -> tuple[list[int], list[Decimal], int, Decimal, int]: + orders: list[int] = [] + sums: list[Decimal] = [] + year, month = start_year, start_month + + for _ in range(YEAR): + order_count = await self._unit_of_work.order_repository.count_by_month( + year=year, + month=month, + search=search, + ) + order_sum = await self._unit_of_work.order_repository.sum_by_month( + year=year, + month=month, + search=search, + ) + + orders.append(order_count) + sums.append(order_sum) + year, month, _ = _count_date(year, month) + return orders, sums, sum(orders), sum(sums), YEAR + + +class GetPurchaseAnalyticsForYearCommand: + def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): + self._unit_of_work = unit_of_work + self._logger = logger + + + @transactional + async def __call__( + self, + *, + start_year: int, + start_month: int | None = None, + status: str | None = None, + organization_id: str | None = None, + ) -> tuple[list[int], int, int]: + requests: list[int] = [] + year, month = start_year, start_month + + for _ in range(YEAR): + request_count = await self._unit_of_work.purchase_request_repository.count_for_month( + year=year, + month=month, + status=status, + organization_id=organization_id, + ) + requests.append(request_count) + year, month, _ = _count_date(year, month) + return requests, sum(requests), YEAR + + +class GetOrdersAnalyticsForMonthCommand: + def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): + self._unit_of_work = unit_of_work + self._logger = logger + + + @transactional + async def __call__( + self, + *, + start_year: int, + start_month: int, + start_day: int | None = None, + search: str | None = None, + ) -> tuple[list[int], list[Decimal], int, Decimal, int]: + orders: list[int] = [] + sums: list[Decimal] = [] + year, month, day = start_year, start_month, start_day + + for _ in range(MONTH): + order_count = await self._unit_of_work.order_repository.count_by_day( + year=year, + month=month, + day=day, + search=search, + ) + order_sum = await self._unit_of_work.order_repository.sum_by_day( + year=year, + month=month, + day=day, + search=search, + ) + + orders.append(order_count) + sums.append(order_sum) + year, month, day = _count_date(year, month, day) + return orders, sums, sum(orders), sum(sums), MONTH + + +class GetPurchaseAnalyticsForMonthCommand: + def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): + self._unit_of_work = unit_of_work + self._logger = logger + + + @transactional + async def __call__( + self, + *, + start_year: int, + start_month: int, + start_day: int | None = None, + status: str | None = None, + organization_id: str | None = None, + ) -> tuple[list[PurchaseRequestEntity], int, int]: + requests: list[int] = [] + year, month, day = start_year, start_month, start_day + + for _ in range(MONTH): + request_count = await self._unit_of_work.purchase_request_repository.count_for_day( + year=year, + month=month, + day=day, + status=status, + organization_id=organization_id, + ) + + requests.append(request_count) + year, month, day = _count_date(year, month, day) + return requests, sum(requests), MONTH + + +class GetPurchaseAnalyticsForWeekCommand: + def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): + self._unit_of_work = unit_of_work + self._logger = logger + + + @transactional + async def __call__( + self, + *, + start_year: int, + start_month: int, + start_day: int, + status: str | None = None, + organization_id: str | None = None, + ) -> tuple[list[PurchaseRequestEntity], int, int]: + requests: list[int] = [] + year, month, day = start_year, start_month, start_day + + for _ in range(WEEK): + request_count = await self._unit_of_work.purchase_request_repository.count_for_day( + year=year, + month=month, + day=day, + status=status, + organization_id=organization_id, + ) + + requests.append(request_count) + year, month, day = _count_date(year, month, day) + return requests, sum(requests), WEEK + + +class GetOrdersAnalyticsForWeekCommand: + def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): + self._unit_of_work = unit_of_work + self._logger = logger + + + @transactional + async def __call__( + self, + *, + start_year: int, + start_month: int, + start_day: int, + search: str | None = None, + ) -> tuple[list[OrderEntity], int, Decimal, int]: + orders: list[int] = [] + sums: list[Decimal] = [] + year, month, day = start_year, start_month, start_day + + for _ in range(WEEK): + order_count = await self._unit_of_work.order_repository.count_by_day( + year=year, + month=month, + day=day, + search=search, + ) + order_sum = await self._unit_of_work.order_repository.sum_by_day( + year=year, + month=month, + day=day, + search=search, + ) + + orders.append(order_count) + sums.append(order_sum) + year, month, day = _count_date(year, month, day) + return orders, sums, sum(orders), sum(sums), WEEK \ No newline at end of file diff --git a/src/application/commands/orders_commands.py b/src/application/commands/orders_commands.py index ad3480f..00a86a3 100644 --- a/src/application/commands/orders_commands.py +++ b/src/application/commands/orders_commands.py @@ -21,7 +21,7 @@ class ListUserOrdersCommand: limit=limit, offset=offset, ) - total = await self._unit_of_work.order_repository.count_by_user_id(user_id=user_id) + total = await self._unit_of_work.order_repository.count_all(search=user_id) return orders, total @@ -52,7 +52,14 @@ class ListOrdersByDateCommand: @transactional - async def __call__(self, *, limit: int, offset: int, year: int, month: int | None = None) -> tuple[list[OrderEntity], int, Decimal]: + async def __call__( + self, + *, + limit: int, + offset: int, + year: int, + month: int | None = None + ) -> tuple[list[OrderEntity], int, Decimal]: orders = await self._unit_of_work.order_repository.list_by_date( year=year, month=month, @@ -60,8 +67,8 @@ class ListOrdersByDateCommand: offset=offset ) - total = await self._unit_of_work.order_repository.count_by_date(year=year, month=month) - summary = await self._unit_of_work.order_repository.sum_by_date(year=year, month=month) + total = await self._unit_of_work.order_repository.count_by_month(year=year, month=month) + summary = await self._unit_of_work.order_repository.sum_by_month(year=year, month=month) return orders, total, summary diff --git a/src/application/domain/entities/__init__.py b/src/application/domain/entities/__init__.py index d81ec5c..7805bc9 100644 --- a/src/application/domain/entities/__init__.py +++ b/src/application/domain/entities/__init__.py @@ -2,5 +2,7 @@ from src.application.domain.entities.user import UserEntity from src.application.domain.entities.admin_user import AdminUserEntity from src.application.domain.entities.admin_session import AdminSessionEntity from src.application.domain.entities.order import OrderEntity +from src.application.domain.entities.organization import PurchaseRequestEntity -__all__ = ['UserEntity', 'AdminUserEntity', 'AdminSessionEntity', 'OrderEntity'] +__all__ = ['UserEntity', 'AdminUserEntity', 'AdminSessionEntity', 'OrderEntity', 'PurchaseRequestEntity'] + \ No newline at end of file diff --git a/src/infrastructure/database/repositories/orders_repository.py b/src/infrastructure/database/repositories/orders_repository.py index 5bc98d6..4442d46 100644 --- a/src/infrastructure/database/repositories/orders_repository.py +++ b/src/infrastructure/database/repositories/orders_repository.py @@ -52,6 +52,7 @@ class OrderRepository(IOrderRepository): return or_( OrderModel.id.ilike(pattern), OrderModel.user_id.ilike(pattern), + OrderModel.status.ilike(pattern), OrderModel.client_payment_id.ilike(pattern), OrderModel.itpay_id.ilike(pattern), OrderModel.itpay_qr_id.ilike(pattern), @@ -72,7 +73,7 @@ class OrderRepository(IOrderRepository): user_id: str, limit: int, offset: int - ) -> list[OrderEntity]: + ) -> list[OrderEntity]: stmt=( select(OrderModel) .where(OrderModel.user_id==user_id) @@ -90,7 +91,7 @@ class OrderRepository(IOrderRepository): limit: int, offset: int, search: str | None = None - ) -> list[OrderEntity]: + ) -> list[OrderEntity]: stmt=( select(OrderModel) .order_by(desc(OrderModel.created_at)) @@ -111,7 +112,7 @@ class OrderRepository(IOrderRepository): month: int, limit: int, offset: int, - ) -> list[OrderEntity]: + ) -> list[OrderEntity]: stmt=( select(OrderModel) .where( @@ -126,7 +127,13 @@ class OrderRepository(IOrderRepository): return [self._to_entity(model) for model in res.scalars().all()] - async def sum_by_date(self, *, year: int, month: int) -> Decimal: + async def sum_by_month( + self, + *, + year: int, + month: int, + search: str | None = None, + ) -> Decimal: stmt = ( select(func.coalesce(func.sum(OrderModel.service_fee), 0)) .where( @@ -135,6 +142,33 @@ class OrderRepository(IOrderRepository): OrderModel.status == OrderStatus.COMPLETED.value, ) ) + search_filter = self._search_filter(search) + if search_filter is not None: + stmt = stmt.where(search_filter) + res = await self._session.execute(stmt) + return res.scalar_one() + + + async def sum_by_day( + self, + *, + year: int, + month: int, + day: int, + search: str | None = None, + ) -> Decimal: + stmt = ( + select(func.coalesce(func.sum(OrderModel.service_fee), 0)) + .where( + extract("year", OrderModel.created_at) == year, + extract("month", OrderModel.created_at) == month, + extract("day", OrderModel.created_at) == day, + OrderModel.status == OrderStatus.COMPLETED.value, + ) + ) + search_filter = self._search_filter(search) + if search_filter is not None: + stmt = stmt.where(search_filter) res = await self._session.execute(stmt) return res.scalar_one() @@ -148,17 +182,28 @@ class OrderRepository(IOrderRepository): return int(res.scalar_one()) - async def count_by_date(self,*,year: int,month: int) -> int: + async def count_by_month(self,*,year: int,month: int, search: str | None = None) -> int: stmt = select(func.count()).select_from(OrderModel) stmt = stmt.where( extract("year", OrderModel.created_at) == year, extract("month", OrderModel.created_at) == month ) + search_filter = self._search_filter(search) + if search_filter is not None: + stmt = stmt.where(search_filter) res = await self._session.execute(stmt) return int(res.scalar_one()) + - - async def count_by_user_id(self,*,user_id: str) -> int: - stmt = select(func.count()).select_from(OrderModel).where(OrderModel.user_id == user_id) + async def count_by_day(self,*,year: int,month: int,day: int, search: str | None = None) -> int: + stmt = select(func.count()).select_from(OrderModel) + stmt = stmt.where( + extract("year", OrderModel.created_at) == year, + extract("month", OrderModel.created_at) == month, + extract("day", OrderModel.created_at) == day + ) + search_filter = self._search_filter(search) + if search_filter is not None: + stmt = stmt.where(search_filter) res = await self._session.execute(stmt) - return int(res.scalar_one()) \ No newline at end of file + return int(res.scalar_one()) diff --git a/src/infrastructure/database/repositories/purchase_request_repository.py b/src/infrastructure/database/repositories/purchase_request_repository.py index aac8421..0376532 100644 --- a/src/infrastructure/database/repositories/purchase_request_repository.py +++ b/src/infrastructure/database/repositories/purchase_request_repository.py @@ -90,3 +90,41 @@ class PurchaseRequestRepository(IPurchaseRequestRepository): ) await self._session.flush() return await self.get_by_id(request_id) + + + async def count_for_month( + self, + *, + year: int, + month: int, + status: str | None, + organization_id: str | None + ) -> int: + stmt = select(func.count()).select_from(PurchaseRequestModel) + stmt = self._apply_filters(stmt, status=status, organization_id=organization_id) + stmt = stmt.where( + func.extract("year", PurchaseRequestModel.created_at) == year, + func.extract("month", PurchaseRequestModel.created_at) == month, + ) + res = await self._session.execute(stmt) + return int(res.scalar_one()) + + + async def count_for_day( + self, + *, + year: int, + month: int, + day: int, + status: str | None, + organization_id: str | None + ) -> int: + stmt = select(func.count()).select_from(PurchaseRequestModel) + stmt = self._apply_filters(stmt, status=status, organization_id=organization_id) + stmt = stmt.where( + func.extract("year", PurchaseRequestModel.created_at) == year, + func.extract("month", PurchaseRequestModel.created_at) == month, + func.extract("day", PurchaseRequestModel.created_at) == day, + ) + res = await self._session.execute(stmt) + return int(res.scalar_one()) diff --git a/src/presentation/dependencies/commands.py b/src/presentation/dependencies/commands.py index c176018..0d574bf 100644 --- a/src/presentation/dependencies/commands.py +++ b/src/presentation/dependencies/commands.py @@ -36,6 +36,12 @@ from src.application.commands import ( ListUserOrdersCommand, ListOrdersByDateCommand, GetOrderCommand, + GetPurchaseAnalyticsForYearCommand, + GetOrdersAnalyticsForYearCommand, + GetPurchaseAnalyticsForMonthCommand, + GetOrdersAnalyticsForMonthCommand, + GetPurchaseAnalyticsForWeekCommand, + GetOrdersAnalyticsForWeekCommand, ) from src.application.contracts import IHashService, IJwtService, ILogger from src.infrastructure.config import settings @@ -277,4 +283,40 @@ def get_get_order_command( uow: IUnitOfWork = Depends(get_unit_of_work), logger: ILogger = Depends(get_logger), ) -> GetOrderCommand: - return GetOrderCommand(uow, logger) \ No newline at end of file + return GetOrderCommand(uow, logger) + +def get_get_purchase_analytics_for_year_command( + uow: IUnitOfWork = Depends(get_unit_of_work), + logger: ILogger = Depends(get_logger), +) -> GetPurchaseAnalyticsForYearCommand: + return GetPurchaseAnalyticsForYearCommand(uow, logger) + +def get_get_orders_analytics_for_year_command( + uow: IUnitOfWork = Depends(get_unit_of_work), + logger: ILogger = Depends(get_logger), +) -> GetOrdersAnalyticsForYearCommand: + return GetOrdersAnalyticsForYearCommand(uow, logger) + +def get_get_purchase_analytics_for_month_command( + uow: IUnitOfWork = Depends(get_unit_of_work), + logger: ILogger = Depends(get_logger), +) -> GetPurchaseAnalyticsForMonthCommand: + return GetPurchaseAnalyticsForMonthCommand(uow, logger) + +def get_get_orders_analytics_for_month_command( + uow: IUnitOfWork = Depends(get_unit_of_work), + logger: ILogger = Depends(get_logger), +) -> GetOrdersAnalyticsForMonthCommand: + return GetOrdersAnalyticsForMonthCommand(uow, logger) + +def get_get_purchase_analytics_for_week_command( + uow: IUnitOfWork = Depends(get_unit_of_work), + logger: ILogger = Depends(get_logger), +) -> GetPurchaseAnalyticsForWeekCommand: + return GetPurchaseAnalyticsForWeekCommand(uow, logger) + +def get_get_orders_analytics_for_week_command( + uow: IUnitOfWork = Depends(get_unit_of_work), + logger: ILogger = Depends(get_logger), +) -> GetOrdersAnalyticsForWeekCommand: + return GetOrdersAnalyticsForWeekCommand(uow, logger) \ No newline at end of file diff --git a/src/presentation/routing/__init__.py b/src/presentation/routing/__init__.py index 92e60c4..5cc1f87 100644 --- a/src/presentation/routing/__init__.py +++ b/src/presentation/routing/__init__.py @@ -8,6 +8,7 @@ from src.presentation.routing.purchase_requests import purchase_requests_router from src.presentation.routing.users import users_router from src.presentation.routing.search import search_router from src.presentation.routing.orders import orders_router +from src.presentation.routing.analytics import analytics_router v1_router = APIRouter(prefix='/v1') v1_router.include_router(auth_router) @@ -17,4 +18,5 @@ v1_router.include_router(documents_router) v1_router.include_router(purchase_requests_router) v1_router.include_router(users_router) v1_router.include_router(search_router) -v1_router.include_router(orders_router) \ No newline at end of file +v1_router.include_router(orders_router) +v1_router.include_router(analytics_router) \ No newline at end of file diff --git a/src/presentation/routing/analytics.py b/src/presentation/routing/analytics.py new file mode 100644 index 0000000..ba05050 --- /dev/null +++ b/src/presentation/routing/analytics.py @@ -0,0 +1,160 @@ +from fastapi import APIRouter, Depends, Request, Query, status + +from src.application.domain.dto import AdminAuthContext +from src.presentation.decorators.admin_auth import require_admin_access, require_admin_role +from src.presentation.dependencies.commands import ( + get_get_purchase_analytics_for_year_command, + get_get_orders_analytics_for_year_command, + get_get_purchase_analytics_for_month_command, + get_get_orders_analytics_for_month_command, + get_get_purchase_analytics_for_week_command, + get_get_orders_analytics_for_week_command, +) +from src.presentation.schemas.analytics import ( + OrderAnalyticsResponse, + PurchaseRequestAnalyticsResponse, +) +from src.application.commands.analytics_commands import ( + GetPurchaseAnalyticsForYearCommand, + GetOrdersAnalyticsForYearCommand, + GetPurchaseAnalyticsForMonthCommand, + GetOrdersAnalyticsForMonthCommand, + GetPurchaseAnalyticsForWeekCommand, + GetOrdersAnalyticsForWeekCommand, +) + +analytics_router = APIRouter(prefix='/analytics', tags=['analytics']) + +@analytics_router.get('/purchases/yearly', response_model=PurchaseRequestAnalyticsResponse) +async def get_purchase_analytics_for_year( + start_year: int = Query(..., ge=2000, le=2100), + start_month: int = Query(..., ge=1, le=12), + auth: AdminAuthContext = Depends(require_admin_access), + status: str | None = Query(default=None, min_length=1, max_length=255), + organization_id: str | None = Query(default=None, min_length=1, max_length=255), + command: GetPurchaseAnalyticsForYearCommand = Depends(get_get_purchase_analytics_for_year_command), +): + items, total, period = await command( + start_year=start_year, + start_month=start_month, + status=status, + organization_id=organization_id, + ) + return PurchaseRequestAnalyticsResponse( + requests_count=items, + total_requests=total, + period=period, + ) + +@analytics_router.get('/orders/yearly', response_model=OrderAnalyticsResponse) +async def get_orders_analytics_for_year( + start_year: int = Query(..., ge=2000, le=2100), + start_month: int = Query(..., ge=1, le=12), + auth: AdminAuthContext = Depends(require_admin_access), + q: str | None = Query(default=None, min_length=1, max_length=255), + command: GetOrdersAnalyticsForYearCommand = Depends(get_get_orders_analytics_for_year_command), +): + orders_count, orders_sum, total_orders, total_sum, period = await command( + start_year=start_year, + start_month=start_month, + search=q, + ) + return OrderAnalyticsResponse( + orders_count=orders_count, + summed_service_fees=orders_sum, + total_orders=total_orders, + total_summed_service_fees=total_sum, + period=period, + ) + +@analytics_router.get('/purchases/monthly', response_model=PurchaseRequestAnalyticsResponse) +async def get_purchase_analytics_for_month( + start_year: int = Query(..., ge=2000, le=2100), + start_month: int = Query(..., ge=1, le=12), + start_day: int = Query(..., ge=1, le=31), + auth: AdminAuthContext = Depends(require_admin_access), + status: str | None = Query(default=None, min_length=1, max_length=255), + organization_id: str | None = Query(default=None, min_length=1, max_length=255), + command: GetPurchaseAnalyticsForMonthCommand = Depends(get_get_purchase_analytics_for_month_command), +): + items, total, period = await command( + start_year=start_year, + start_month=start_month, + start_day=start_day, + status=status, + organization_id=organization_id, + ) + return PurchaseRequestAnalyticsResponse( + requests_count=items, + total_requests=total, + period=period, + ) + +@analytics_router.get('/orders/monthly', response_model=OrderAnalyticsResponse) +async def get_orders_analytics_for_month( + start_year: int = Query(..., ge=2000, le=2100), + start_month: int = Query(..., ge=1, le=12), + start_day: int = Query(..., ge=1, le=31), + auth: AdminAuthContext = Depends(require_admin_access), + q: str | None = Query(default=None, min_length=1, max_length=255), + command: GetOrdersAnalyticsForMonthCommand = Depends(get_get_orders_analytics_for_month_command), +): + orders_count, orders_sum, total_orders, total_sum, period = await command( + start_year=start_year, + start_month=start_month, + start_day=start_day, + search=q, + ) + return OrderAnalyticsResponse( + orders_count=orders_count, + summed_service_fees=orders_sum, + total_orders=total_orders, + total_summed_service_fees=total_sum, + period=period, + ) + +@analytics_router.get('/purchases/weekly', response_model=PurchaseRequestAnalyticsResponse) +async def get_purchase_analytics_for_week( + start_year: int = Query(..., ge=2000, le=2100), + start_month: int = Query(..., ge=1, le=12), + start_day: int = Query(..., ge=1, le=31), + auth: AdminAuthContext = Depends(require_admin_access), + status: str | None = Query(default=None, min_length=1, max_length=255), + organization_id: str | None = Query(default=None, min_length=1, max_length=255), + command: GetPurchaseAnalyticsForWeekCommand = Depends(get_get_purchase_analytics_for_week_command), +): + items, total, period = await command( + start_year=start_year, + start_month=start_month, + start_day=start_day, + status=status, + organization_id=organization_id, + ) + return PurchaseRequestAnalyticsResponse( + requests_count=items, + total_requests=total, + period=period, + ) + +@analytics_router.get('/orders/weekly', response_model=OrderAnalyticsResponse) +async def get_orders_analytics_for_week( + start_year: int = Query(..., ge=2000, le=2100), + start_month: int = Query(..., ge=1, le=12), + start_day: int = Query(..., ge=1, le=31), + auth: AdminAuthContext = Depends(require_admin_access), + q: str | None = Query(default=None, min_length=1, max_length=255), + command: GetOrdersAnalyticsForWeekCommand = Depends(get_get_orders_analytics_for_week_command), +): + orders_count, orders_sum, total_orders, total_sum, period = await command( + start_year=start_year, + start_month=start_month, + start_day=start_day, + search=q, + ) + return OrderAnalyticsResponse( + orders_count=orders_count, + summed_service_fees=orders_sum, + total_orders=total_orders, + total_summed_service_fees=total_sum, + period=period, + ) \ No newline at end of file diff --git a/src/presentation/schemas/analytics.py b/src/presentation/schemas/analytics.py new file mode 100644 index 0000000..4a51a6b --- /dev/null +++ b/src/presentation/schemas/analytics.py @@ -0,0 +1,15 @@ +from decimal import Decimal +from pydantic import BaseModel + +class OrderAnalyticsResponse(BaseModel): + orders_count: list[int] + summed_service_fees: list[Decimal] + total_orders: int + total_summed_service_fees: Decimal + period: int | None = None + + +class PurchaseRequestAnalyticsResponse(BaseModel): + requests_count: list[int] + total_requests: int + period: int | None = None \ No newline at end of file