mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-07 21:10:46 -05:00
90dde470da
- Normalize line endings from CRLF to LF across all files to match .editorconfig - Standardize quote style from single quotes to double quotes - Normalize whitespace and formatting throughout codebase - Apply consistent code style across 372 files including: * Application code (models, routes, services, utils) * Test files * Configuration files * CI/CD workflows This ensures consistency with the project's .editorconfig settings and improves code maintainability.
181 lines
5.9 KiB
Python
181 lines
5.9 KiB
Python
"""
|
|
Base CRUD service to reduce code duplication across services.
|
|
Provides common CRUD operations with consistent error handling.
|
|
"""
|
|
|
|
from typing import TypeVar, Generic, Optional, Dict, Any, List
|
|
from app import db
|
|
from app.utils.db import safe_commit
|
|
from app.utils.api_responses import error_response
|
|
|
|
ModelType = TypeVar("ModelType")
|
|
RepositoryType = TypeVar("RepositoryType")
|
|
|
|
|
|
class BaseCRUDService(Generic[ModelType, RepositoryType]):
|
|
"""
|
|
Base service class providing common CRUD operations.
|
|
|
|
Subclasses should set:
|
|
- self.repository: The repository instance
|
|
- self.model_name: Human-readable model name for error messages
|
|
"""
|
|
|
|
def __init__(self, repository: RepositoryType, model_name: str = "Record"):
|
|
"""
|
|
Initialize base CRUD service.
|
|
|
|
Args:
|
|
repository: Repository instance for data access
|
|
model_name: Human-readable name for error messages
|
|
"""
|
|
self.repository = repository
|
|
self.model_name = model_name
|
|
|
|
def get_by_id(self, record_id: int) -> Dict[str, Any]:
|
|
"""
|
|
Get a record by ID.
|
|
|
|
Args:
|
|
record_id: The record ID
|
|
|
|
Returns:
|
|
dict with 'success', 'message', and record data
|
|
"""
|
|
record = self.repository.get_by_id(record_id)
|
|
|
|
if not record:
|
|
return {"success": False, "message": f"{self.model_name} not found", "error": "not_found"}
|
|
|
|
return {"success": True, "message": f"{self.model_name} retrieved successfully", "data": record}
|
|
|
|
def create(self, **kwargs) -> Dict[str, Any]:
|
|
"""
|
|
Create a new record.
|
|
|
|
Args:
|
|
**kwargs: Fields for the new record
|
|
|
|
Returns:
|
|
dict with 'success', 'message', and created record
|
|
"""
|
|
try:
|
|
record = self.repository.create(**kwargs)
|
|
|
|
if not safe_commit(f"create_{self.model_name.lower()}", kwargs):
|
|
return {
|
|
"success": False,
|
|
"message": f"Could not create {self.model_name.lower()} due to a database error",
|
|
"error": "database_error",
|
|
}
|
|
|
|
return {"success": True, "message": f"{self.model_name} created successfully", "data": record}
|
|
except Exception as e:
|
|
return {
|
|
"success": False,
|
|
"message": f"Error creating {self.model_name.lower()}: {str(e)}",
|
|
"error": "creation_error",
|
|
}
|
|
|
|
def update(self, record_id: int, **kwargs) -> Dict[str, Any]:
|
|
"""
|
|
Update an existing record.
|
|
|
|
Args:
|
|
record_id: The record ID
|
|
**kwargs: Fields to update
|
|
|
|
Returns:
|
|
dict with 'success', 'message', and updated record
|
|
"""
|
|
record = self.repository.get_by_id(record_id)
|
|
|
|
if not record:
|
|
return {"success": False, "message": f"{self.model_name} not found", "error": "not_found"}
|
|
|
|
try:
|
|
self.repository.update(record, **kwargs)
|
|
|
|
if not safe_commit(f"update_{self.model_name.lower()}", {"record_id": record_id}):
|
|
return {
|
|
"success": False,
|
|
"message": f"Could not update {self.model_name.lower()} due to a database error",
|
|
"error": "database_error",
|
|
}
|
|
|
|
return {"success": True, "message": f"{self.model_name} updated successfully", "data": record}
|
|
except Exception as e:
|
|
return {
|
|
"success": False,
|
|
"message": f"Error updating {self.model_name.lower()}: {str(e)}",
|
|
"error": "update_error",
|
|
}
|
|
|
|
def delete(self, record_id: int) -> Dict[str, Any]:
|
|
"""
|
|
Delete a record.
|
|
|
|
Args:
|
|
record_id: The record ID
|
|
|
|
Returns:
|
|
dict with 'success' and 'message'
|
|
"""
|
|
record = self.repository.get_by_id(record_id)
|
|
|
|
if not record:
|
|
return {"success": False, "message": f"{self.model_name} not found", "error": "not_found"}
|
|
|
|
try:
|
|
if not self.repository.delete(record):
|
|
return {
|
|
"success": False,
|
|
"message": f"Could not delete {self.model_name.lower()}",
|
|
"error": "delete_error",
|
|
}
|
|
|
|
if not safe_commit(f"delete_{self.model_name.lower()}", {"record_id": record_id}):
|
|
return {
|
|
"success": False,
|
|
"message": f"Could not delete {self.model_name.lower()} due to a database error",
|
|
"error": "database_error",
|
|
}
|
|
|
|
return {"success": True, "message": f"{self.model_name} deleted successfully"}
|
|
except Exception as e:
|
|
return {
|
|
"success": False,
|
|
"message": f"Error deleting {self.model_name.lower()}: {str(e)}",
|
|
"error": "delete_error",
|
|
}
|
|
|
|
def list_all(self, page: int = 1, per_page: int = 20, **filters) -> Dict[str, Any]:
|
|
"""
|
|
List all records with pagination and optional filters.
|
|
|
|
Args:
|
|
page: Page number
|
|
per_page: Records per page
|
|
**filters: Filter criteria
|
|
|
|
Returns:
|
|
dict with 'success', 'data', 'pagination', and 'total'
|
|
"""
|
|
try:
|
|
query = self.repository.query()
|
|
|
|
# Apply filters
|
|
if filters:
|
|
query = query.filter_by(**filters)
|
|
|
|
# Paginate
|
|
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
|
|
|
return {"success": True, "data": pagination.items, "pagination": pagination, "total": pagination.total}
|
|
except Exception as e:
|
|
return {
|
|
"success": False,
|
|
"message": f"Error listing {self.model_name.lower()}: {str(e)}",
|
|
"error": "list_error",
|
|
}
|