Files
syndarix/backend/app/schemas/common.py
Felipe Cardoso c589b565f0 Add pyproject.toml for consolidated project configuration and replace Black, isort, and Flake8 with Ruff
- Introduced `pyproject.toml` to centralize backend tool configurations (e.g., Ruff, mypy, coverage, pytest).
- Replaced Black, isort, and Flake8 with Ruff for linting, formatting, and import sorting.
- Updated `requirements.txt` to include Ruff and remove replaced tools.
- Added `Makefile` to streamline development workflows with commands for linting, formatting, type-checking, testing, and cleanup.
2025-11-10 11:55:15 +01:00

185 lines
4.9 KiB
Python

"""
Common schemas used across the API for pagination, responses, filtering, and sorting.
"""
from enum import Enum
from math import ceil
from typing import TypeVar
from uuid import UUID
from pydantic import BaseModel, Field
T = TypeVar("T")
class SortOrder(str, Enum):
"""Sort order options."""
ASC = "asc"
DESC = "desc"
class PaginationParams(BaseModel):
"""Parameters for pagination."""
page: int = Field(default=1, ge=1, description="Page number (1-indexed)")
limit: int = Field(
default=20, ge=1, le=100, description="Number of items per page (max 100)"
)
@property
def offset(self) -> int:
"""Calculate the offset for database queries."""
return (self.page - 1) * self.limit
@property
def skip(self) -> int:
"""Alias for offset (compatibility with existing code)."""
return self.offset
model_config = {"json_schema_extra": {"example": {"page": 1, "limit": 20}}}
class SortParams(BaseModel):
"""Parameters for sorting."""
sort_by: str | None = Field(default=None, description="Field name to sort by")
sort_order: SortOrder = Field(
default=SortOrder.ASC, description="Sort order (asc or desc)"
)
model_config = {
"json_schema_extra": {
"example": {"sort_by": "created_at", "sort_order": "desc"}
}
}
class PaginationMeta(BaseModel):
"""Metadata for paginated responses."""
total: int = Field(..., description="Total number of items")
page: int = Field(..., description="Current page number")
page_size: int = Field(..., description="Number of items in current page")
total_pages: int = Field(..., description="Total number of pages")
has_next: bool = Field(..., description="Whether there is a next page")
has_prev: bool = Field(..., description="Whether there is a previous page")
model_config = {
"json_schema_extra": {
"example": {
"total": 150,
"page": 1,
"page_size": 20,
"total_pages": 8,
"has_next": True,
"has_prev": False,
}
}
}
class PaginatedResponse[T](BaseModel):
"""Generic paginated response wrapper."""
data: list[T] = Field(..., description="List of items")
pagination: PaginationMeta = Field(..., description="Pagination metadata")
model_config = {
"json_schema_extra": {
"example": {
"data": [{"id": "123", "name": "Example Item"}],
"pagination": {
"total": 150,
"page": 1,
"page_size": 20,
"total_pages": 8,
"has_next": True,
"has_prev": False,
},
}
}
}
class MessageResponse(BaseModel):
"""Simple message response."""
success: bool = Field(default=True, description="Operation success status")
message: str = Field(..., description="Human-readable message")
model_config = {
"json_schema_extra": {
"example": {"success": True, "message": "Operation completed successfully"}
}
}
class BulkActionRequest(BaseModel):
"""Request schema for bulk operations on multiple items."""
ids: list[UUID] = Field(
...,
min_length=1,
max_length=100,
description="List of item IDs to perform action on (max 100)",
)
model_config = {
"json_schema_extra": {
"example": {
"ids": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8",
]
}
}
}
class BulkActionResponse(BaseModel):
"""Response schema for bulk operations."""
success: bool = Field(default=True, description="Operation success status")
message: str = Field(..., description="Human-readable message")
affected_count: int = Field(
..., description="Number of items affected by the operation"
)
model_config = {
"json_schema_extra": {
"example": {
"success": True,
"message": "Successfully deactivated 5 users",
"affected_count": 5,
}
}
}
def create_pagination_meta(
total: int, page: int, limit: int, items_count: int
) -> PaginationMeta:
"""
Helper function to create pagination metadata.
Args:
total: Total number of items
page: Current page number
limit: Items per page
items_count: Number of items in current page
Returns:
PaginationMeta object with calculated values
"""
total_pages = ceil(total / limit) if limit > 0 else 0
return PaginationMeta(
total=total,
page=page,
page_size=items_count,
total_pages=total_pages,
has_next=page < total_pages,
has_prev=page > 1,
)