mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 14:31:08 -05:00
225 lines
6.1 KiB
Python
225 lines
6.1 KiB
Python
"""
|
|
Custom exception classes for ThrillWiki.
|
|
Provides domain-specific exceptions with proper error codes and messages.
|
|
"""
|
|
|
|
from typing import Optional, Dict, Any
|
|
|
|
|
|
class ThrillWikiException(Exception):
|
|
"""Base exception for all ThrillWiki-specific errors."""
|
|
|
|
default_message = "An error occurred"
|
|
error_code = "THRILLWIKI_ERROR"
|
|
status_code = 500
|
|
|
|
def __init__(
|
|
self,
|
|
message: Optional[str] = None,
|
|
error_code: Optional[str] = None,
|
|
details: Optional[Dict[str, Any]] = None,
|
|
):
|
|
self.message = message or self.default_message
|
|
self.error_code = error_code or self.error_code
|
|
self.details = details or {}
|
|
super().__init__(self.message)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert exception to dictionary for API responses."""
|
|
return {
|
|
"error_code": self.error_code,
|
|
"message": self.message,
|
|
"details": self.details,
|
|
}
|
|
|
|
|
|
class ValidationException(ThrillWikiException):
|
|
"""Raised when data validation fails."""
|
|
|
|
default_message = "Validation failed"
|
|
error_code = "VALIDATION_ERROR"
|
|
status_code = 400
|
|
|
|
|
|
class NotFoundError(ThrillWikiException):
|
|
"""Raised when a requested resource is not found."""
|
|
|
|
default_message = "Resource not found"
|
|
error_code = "NOT_FOUND"
|
|
status_code = 404
|
|
|
|
|
|
class PermissionDeniedError(ThrillWikiException):
|
|
"""Raised when user lacks permission for an operation."""
|
|
|
|
default_message = "Permission denied"
|
|
error_code = "PERMISSION_DENIED"
|
|
status_code = 403
|
|
|
|
|
|
class BusinessLogicError(ThrillWikiException):
|
|
"""Raised when business logic constraints are violated."""
|
|
|
|
default_message = "Business logic violation"
|
|
error_code = "BUSINESS_LOGIC_ERROR"
|
|
status_code = 400
|
|
|
|
|
|
class ExternalServiceError(ThrillWikiException):
|
|
"""Raised when external service calls fail."""
|
|
|
|
default_message = "External service error"
|
|
error_code = "EXTERNAL_SERVICE_ERROR"
|
|
status_code = 502
|
|
|
|
|
|
# Domain-specific exceptions
|
|
|
|
|
|
class ParkError(ThrillWikiException):
|
|
"""Base exception for park-related errors."""
|
|
|
|
error_code = "PARK_ERROR"
|
|
|
|
|
|
class ParkNotFoundError(NotFoundError):
|
|
"""Raised when a park is not found."""
|
|
|
|
default_message = "Park not found"
|
|
error_code = "PARK_NOT_FOUND"
|
|
|
|
def __init__(self, park_slug: Optional[str] = None, **kwargs):
|
|
if park_slug:
|
|
kwargs["details"] = {"park_slug": park_slug}
|
|
kwargs["message"] = f"Park with slug '{park_slug}' not found"
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
class ParkOperationError(BusinessLogicError):
|
|
"""Raised when park operation constraints are violated."""
|
|
|
|
default_message = "Invalid park operation"
|
|
error_code = "PARK_OPERATION_ERROR"
|
|
|
|
|
|
class RideError(ThrillWikiException):
|
|
"""Base exception for ride-related errors."""
|
|
|
|
error_code = "RIDE_ERROR"
|
|
|
|
|
|
class RideNotFoundError(NotFoundError):
|
|
"""Raised when a ride is not found."""
|
|
|
|
default_message = "Ride not found"
|
|
error_code = "RIDE_NOT_FOUND"
|
|
|
|
def __init__(self, ride_slug: Optional[str] = None, **kwargs):
|
|
if ride_slug:
|
|
kwargs["details"] = {"ride_slug": ride_slug}
|
|
kwargs["message"] = f"Ride with slug '{ride_slug}' not found"
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
class RideOperationError(BusinessLogicError):
|
|
"""Raised when ride operation constraints are violated."""
|
|
|
|
default_message = "Invalid ride operation"
|
|
error_code = "RIDE_OPERATION_ERROR"
|
|
|
|
|
|
class LocationError(ThrillWikiException):
|
|
"""Base exception for location-related errors."""
|
|
|
|
error_code = "LOCATION_ERROR"
|
|
|
|
|
|
class InvalidCoordinatesError(ValidationException):
|
|
"""Raised when geographic coordinates are invalid."""
|
|
|
|
default_message = "Invalid geographic coordinates"
|
|
error_code = "INVALID_COORDINATES"
|
|
|
|
def __init__(
|
|
self,
|
|
latitude: Optional[float] = None,
|
|
longitude: Optional[float] = None,
|
|
**kwargs,
|
|
):
|
|
if latitude is not None or longitude is not None:
|
|
kwargs["details"] = {"latitude": latitude, "longitude": longitude}
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
class GeolocationError(ExternalServiceError):
|
|
"""Raised when geolocation services fail."""
|
|
|
|
default_message = "Geolocation service unavailable"
|
|
error_code = "GEOLOCATION_ERROR"
|
|
|
|
|
|
class ReviewError(ThrillWikiException):
|
|
"""Base exception for review-related errors."""
|
|
|
|
error_code = "REVIEW_ERROR"
|
|
|
|
|
|
class ReviewModerationError(BusinessLogicError):
|
|
"""Raised when review moderation constraints are violated."""
|
|
|
|
default_message = "Review moderation error"
|
|
error_code = "REVIEW_MODERATION_ERROR"
|
|
|
|
|
|
class DuplicateReviewError(BusinessLogicError):
|
|
"""Raised when user tries to create duplicate reviews."""
|
|
|
|
default_message = "User has already reviewed this item"
|
|
error_code = "DUPLICATE_REVIEW"
|
|
|
|
|
|
class AccountError(ThrillWikiException):
|
|
"""Base exception for account-related errors."""
|
|
|
|
error_code = "ACCOUNT_ERROR"
|
|
|
|
|
|
class InsufficientPermissionsError(PermissionDeniedError):
|
|
"""Raised when user lacks required permissions."""
|
|
|
|
default_message = "Insufficient permissions"
|
|
error_code = "INSUFFICIENT_PERMISSIONS"
|
|
|
|
def __init__(self, required_permission: Optional[str] = None, **kwargs):
|
|
if required_permission:
|
|
kwargs["details"] = {"required_permission": required_permission}
|
|
kwargs["message"] = f"Permission '{required_permission}' required"
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
class EmailError(ExternalServiceError):
|
|
"""Raised when email operations fail."""
|
|
|
|
default_message = "Email service error"
|
|
error_code = "EMAIL_ERROR"
|
|
|
|
|
|
class CacheError(ThrillWikiException):
|
|
"""Raised when cache operations fail."""
|
|
|
|
default_message = "Cache operation failed"
|
|
error_code = "CACHE_ERROR"
|
|
status_code = 500
|
|
|
|
|
|
class RoadTripError(ExternalServiceError):
|
|
"""Raised when road trip planning fails."""
|
|
|
|
default_message = "Road trip planning error"
|
|
error_code = "ROADTRIP_ERROR"
|
|
|
|
def __init__(self, service_name: Optional[str] = None, **kwargs):
|
|
if service_name:
|
|
kwargs["details"] = {"service": service_name}
|
|
super().__init__(**kwargs)
|