people endpoints and super user events built
This commit is contained in:
@@ -23,9 +23,17 @@ class BaseAlchemyModel:
|
||||
|
||||
__abstract__ = True
|
||||
|
||||
@classmethod
|
||||
def refresh_class_attributes(cls):
|
||||
cls.pre_query = None
|
||||
cls.meta_data.created = False
|
||||
cls.meta_data.updated = False
|
||||
cls.meta_data.deleted = False
|
||||
|
||||
@classmethod
|
||||
def new_session(cls):
|
||||
"""Get database session."""
|
||||
cls.refresh_class_attributes()
|
||||
return get_db()
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -48,6 +48,8 @@ def get_db() -> Generator[Session, None, None]:
|
||||
Yields:
|
||||
Session: SQLAlchemy session object
|
||||
"""
|
||||
from Controllers.Postgres.mixin import CrudMixin
|
||||
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
try:
|
||||
|
||||
@@ -55,7 +55,6 @@ class QueryModel:
|
||||
new_args = list(
|
||||
dict.fromkeys(arg for arg in args_list if isinstance(arg, BinaryExpression))
|
||||
)
|
||||
|
||||
# Check if argument already exists
|
||||
if not any(
|
||||
getattr(getattr(arg, "left", None), "key", None) == argument
|
||||
@@ -97,7 +96,6 @@ class QueryModel:
|
||||
):
|
||||
starts = cls.expiry_starts <= current_time
|
||||
args = cls.add_new_arg_to_args(args, "expiry_starts", starts)
|
||||
|
||||
return args
|
||||
|
||||
except AttributeError as e:
|
||||
|
||||
231
Controllers/Postgres/pagination.py
Normal file
231
Controllers/Postgres/pagination.py
Normal file
@@ -0,0 +1,231 @@
|
||||
from typing import Any, Dict, Optional, Union, TypeVar, Type
|
||||
from sqlalchemy import desc, asc
|
||||
from pydantic import BaseModel
|
||||
|
||||
from Controllers.Postgres.response import PostgresResponse
|
||||
|
||||
# Type variable for class methods returning self
|
||||
T = TypeVar("T", bound="BaseModel")
|
||||
|
||||
|
||||
class PaginateConfig:
|
||||
"""
|
||||
Configuration for pagination settings.
|
||||
|
||||
Attributes:
|
||||
DEFAULT_SIZE: Default number of items per page (10)
|
||||
MIN_SIZE: Minimum allowed page size (10)
|
||||
MAX_SIZE: Maximum allowed page size (40)
|
||||
"""
|
||||
|
||||
DEFAULT_SIZE = 10
|
||||
MIN_SIZE = 1
|
||||
MAX_SIZE = 100
|
||||
|
||||
|
||||
class ListOptions(BaseModel):
|
||||
"""
|
||||
Query for list option abilities
|
||||
"""
|
||||
|
||||
page: Optional[int] = 1
|
||||
size: Optional[int] = 10
|
||||
order_field: Optional[Union[tuple[str], list[str]]] = ["uu_id"]
|
||||
order_type: Optional[Union[tuple[str], list[str]]] = ["asc"]
|
||||
# include_joins: Optional[list] = None
|
||||
|
||||
|
||||
class PaginateOnly(ListOptions):
|
||||
"""
|
||||
Query for list option abilities
|
||||
"""
|
||||
|
||||
query: Optional[dict] = None
|
||||
|
||||
|
||||
class PaginationConfig(BaseModel):
|
||||
"""
|
||||
Configuration for pagination settings.
|
||||
|
||||
Attributes:
|
||||
page: Current page number (default: 1)
|
||||
size: Items per page (default: 10)
|
||||
order_field: Field to order by (default: "id")
|
||||
order_type: Order direction (default: "asc")
|
||||
"""
|
||||
|
||||
page: int = 1
|
||||
size: int = 10
|
||||
order_field: Optional[Union[tuple[str], list[str]]] = ["uu_id"]
|
||||
order_type: Optional[Union[tuple[str], list[str]]] = ["asc"]
|
||||
|
||||
def __init__(self, **data):
|
||||
super().__init__(**data)
|
||||
if self.order_field is None:
|
||||
self.order_field = ["uu_id"]
|
||||
if self.order_type is None:
|
||||
self.order_type = ["asc"]
|
||||
|
||||
|
||||
class Pagination:
|
||||
"""
|
||||
Handles pagination logic for query results.
|
||||
|
||||
Manages page size, current page, ordering, and calculates total pages
|
||||
and items based on the data source.
|
||||
|
||||
Attributes:
|
||||
DEFAULT_SIZE: Default number of items per page (10)
|
||||
MIN_SIZE: Minimum allowed page size (10)
|
||||
MAX_SIZE: Maximum allowed page size (40)
|
||||
"""
|
||||
|
||||
DEFAULT_SIZE = PaginateConfig.DEFAULT_SIZE
|
||||
MIN_SIZE = PaginateConfig.MIN_SIZE
|
||||
MAX_SIZE = PaginateConfig.MAX_SIZE
|
||||
|
||||
def __init__(self, data: PostgresResponse):
|
||||
self.data = data
|
||||
self.size: int = self.DEFAULT_SIZE
|
||||
self.page: int = 1
|
||||
self.orderField: Optional[Union[tuple[str], list[str]]] = ["uu_id"]
|
||||
self.orderType: Optional[Union[tuple[str], list[str]]] = ["asc"]
|
||||
self.page_count: int = 1
|
||||
self.total_count: int = 0
|
||||
self.all_count: int = 0
|
||||
self.total_pages: int = 1
|
||||
self._update_page_counts()
|
||||
|
||||
def change(self, **kwargs) -> None:
|
||||
"""Update pagination settings from config."""
|
||||
config = PaginationConfig(**kwargs)
|
||||
self.size = (
|
||||
config.size
|
||||
if self.MIN_SIZE <= config.size <= self.MAX_SIZE
|
||||
else self.DEFAULT_SIZE
|
||||
)
|
||||
self.page = config.page
|
||||
self.orderField = config.order_field
|
||||
self.orderType = config.order_type
|
||||
self._update_page_counts()
|
||||
|
||||
def feed(self, data: PostgresResponse) -> None:
|
||||
"""Calculate pagination based on data source."""
|
||||
self.data = data
|
||||
self._update_page_counts()
|
||||
|
||||
def _update_page_counts(self) -> None:
|
||||
"""Update page counts and validate current page."""
|
||||
if self.data:
|
||||
self.total_count = self.data.count
|
||||
self.all_count = self.data.total_count
|
||||
|
||||
self.size = (
|
||||
self.size
|
||||
if self.MIN_SIZE <= self.size <= self.MAX_SIZE
|
||||
else self.DEFAULT_SIZE
|
||||
)
|
||||
self.total_pages = max(1, (self.total_count + self.size - 1) // self.size)
|
||||
self.page = max(1, min(self.page, self.total_pages))
|
||||
self.page_count = (
|
||||
self.total_count % self.size
|
||||
if self.page == self.total_pages and self.total_count % self.size
|
||||
else self.size
|
||||
)
|
||||
|
||||
def refresh(self) -> None:
|
||||
"""Reset pagination state to defaults."""
|
||||
self._update_page_counts()
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset pagination state to defaults."""
|
||||
self.size = self.DEFAULT_SIZE
|
||||
self.page = 1
|
||||
self.orderField = "uu_id"
|
||||
self.orderType = "asc"
|
||||
|
||||
def as_dict(self) -> Dict[str, Any]:
|
||||
"""Convert pagination state to dictionary format."""
|
||||
self.refresh()
|
||||
return {
|
||||
"size": self.size,
|
||||
"page": self.page,
|
||||
"allCount": self.all_count,
|
||||
"totalCount": self.total_count,
|
||||
"totalPages": self.total_pages,
|
||||
"pageCount": self.page_count,
|
||||
"orderField": self.orderField,
|
||||
"orderType": self.orderType,
|
||||
}
|
||||
|
||||
|
||||
class PaginationResult:
|
||||
"""
|
||||
Result of a paginated query.
|
||||
|
||||
Contains the query result and pagination state.
|
||||
data: PostgresResponse of query results
|
||||
pagination: Pagination state
|
||||
|
||||
Attributes:
|
||||
_query: Original query object
|
||||
pagination: Pagination state
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: PostgresResponse,
|
||||
pagination: Pagination,
|
||||
response_model: Type[T] = None,
|
||||
):
|
||||
self._query = data.query
|
||||
self.pagination = pagination
|
||||
self.response_type = data.is_list
|
||||
self.limit = self.pagination.size
|
||||
self.offset = self.pagination.size * (self.pagination.page - 1)
|
||||
self.order_by = self.pagination.orderField
|
||||
self.response_model = response_model
|
||||
|
||||
def dynamic_order_by(self):
|
||||
"""
|
||||
Dynamically order a query by multiple fields.
|
||||
Returns:
|
||||
Ordered query object.
|
||||
"""
|
||||
if not len(self.order_by) == len(self.pagination.orderType):
|
||||
raise ValueError(
|
||||
"Order by fields and order types must have the same length."
|
||||
)
|
||||
order_criteria = zip(self.order_by, self.pagination.orderType)
|
||||
for field, direction in order_criteria:
|
||||
if hasattr(self._query.column_descriptions[0]["entity"], field):
|
||||
if direction.lower().startswith("d"):
|
||||
self._query = self._query.order_by(
|
||||
desc(
|
||||
getattr(self._query.column_descriptions[0]["entity"], field)
|
||||
)
|
||||
)
|
||||
else:
|
||||
self._query = self._query.order_by(
|
||||
asc(
|
||||
getattr(self._query.column_descriptions[0]["entity"], field)
|
||||
)
|
||||
)
|
||||
return self._query
|
||||
|
||||
@property
|
||||
def data(self) -> Union[list | dict]:
|
||||
"""Get query object."""
|
||||
query_ordered = self.dynamic_order_by()
|
||||
query_paginated = query_ordered.limit(self.limit).offset(self.offset)
|
||||
queried_data = (
|
||||
query_paginated.all() if self.response_type else query_paginated.first()
|
||||
)
|
||||
data = (
|
||||
[result.get_dict() for result in queried_data]
|
||||
if self.response_type
|
||||
else queried_data.get_dict()
|
||||
)
|
||||
if self.response_model:
|
||||
return [self.response_model(**item).model_dump() for item in data]
|
||||
return data
|
||||
@@ -6,6 +6,8 @@ adding convenience methods for accessing data and managing query state.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional, TypeVar, Generic, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Query
|
||||
|
||||
|
||||
@@ -107,3 +109,19 @@ class PostgresResponse(Generic[T]):
|
||||
"count": self.count,
|
||||
"data": self.data.get_dict() if self.data else {},
|
||||
}
|
||||
|
||||
|
||||
class EndpointResponse(BaseModel):
|
||||
completed: bool = True
|
||||
message: str = "Success"
|
||||
pagination_result: Any
|
||||
|
||||
@property
|
||||
def response(self):
|
||||
"""Convert response to dictionary format."""
|
||||
return {
|
||||
"completed": self.completed,
|
||||
"message": self.message,
|
||||
"data": self.pagination_result.data,
|
||||
"pagination": self.pagination_result.pagination.as_dict(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user