File size: 8,401 Bytes
453520f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
"""
Custom exception classes for EVG Ultimate Team backend.

This module defines custom exceptions for different error scenarios,
making error handling more explicit and maintainable throughout the application.

All custom exceptions inherit from EVGException base class and include
HTTP status codes for API responses.
"""

from typing import Optional


class EVGException(Exception):
    """
    Base exception class for all EVG Ultimate Team exceptions.

    Attributes:
        message: Human-readable error message
        status_code: HTTP status code for API responses
        detail: Optional additional details for debugging
    """

    def __init__(
        self,
        message: str,
        status_code: int = 500,
        detail: Optional[str] = None
    ):
        self.message = message
        self.status_code = status_code
        self.detail = detail
        super().__init__(self.message)


# =============================================================================
# Authentication Exceptions
# =============================================================================

class AuthenticationError(EVGException):
    """Raised when authentication fails."""

    def __init__(
        self,
        message: str = "Authentication failed",
        detail: Optional[str] = None
    ):
        super().__init__(message=message, status_code=401, detail=detail)


class InvalidCredentialsError(AuthenticationError):
    """Raised when login credentials are invalid."""

    def __init__(self, detail: Optional[str] = None):
        super().__init__(
            message="Invalid username or password",
            detail=detail
        )


class UnauthorizedError(EVGException):
    """Raised when user lacks permission for an action."""

    def __init__(
        self,
        message: str = "You don't have permission to perform this action",
        detail: Optional[str] = None
    ):
        super().__init__(message=message, status_code=403, detail=detail)


class AdminRequiredError(UnauthorizedError):
    """Raised when admin privileges are required but not present."""

    def __init__(self):
        super().__init__(
            message="Admin privileges required for this action",
            detail="Only administrators can perform this operation"
        )


# =============================================================================
# Resource Not Found Exceptions
# =============================================================================

class NotFoundError(EVGException):
    """Base class for resource not found errors."""

    def __init__(
        self,
        resource_type: str,
        resource_id: Optional[int] = None,
        detail: Optional[str] = None
    ):
        if resource_id:
            message = f"{resource_type} with ID {resource_id} not found"
        else:
            message = f"{resource_type} not found"

        super().__init__(message=message, status_code=404, detail=detail)


class ParticipantNotFoundError(NotFoundError):
    """Raised when a participant cannot be found."""

    def __init__(self, participant_id: Optional[int] = None):
        super().__init__(
            resource_type="Participant",
            resource_id=participant_id
        )


class ChallengeNotFoundError(NotFoundError):
    """Raised when a challenge cannot be found."""

    def __init__(self, challenge_id: Optional[int] = None):
        super().__init__(
            resource_type="Challenge",
            resource_id=challenge_id
        )


class PackNotFoundError(NotFoundError):
    """Raised when a pack cannot be found."""

    def __init__(self, pack_id: Optional[int] = None):
        super().__init__(
            resource_type="Pack",
            resource_id=pack_id
        )


class EventNotFoundError(NotFoundError):
    """Raised when an event card cannot be found."""

    def __init__(self, event_id: Optional[int] = None):
        super().__init__(
            resource_type="Event",
            resource_id=event_id
        )


# =============================================================================
# Validation Exceptions
# =============================================================================

class ValidationError(EVGException):
    """Raised when input validation fails."""

    def __init__(
        self,
        message: str = "Validation error",
        detail: Optional[str] = None
    ):
        super().__init__(message=message, status_code=422, detail=detail)


class InsufficientPointsError(ValidationError):
    """Raised when participant doesn't have enough points for an action."""

    def __init__(self, required_points: int, current_points: int):
        super().__init__(
            message="Insufficient points for this action",
            detail=f"Required: {required_points} points, Current: {current_points} points"
        )


class InvalidChallengeStatusError(ValidationError):
    """Raised when attempting an invalid challenge status transition."""

    def __init__(self, current_status: str, attempted_status: str):
        super().__init__(
            message="Invalid challenge status transition",
            detail=f"Cannot change from '{current_status}' to '{attempted_status}'"
        )


class DuplicateEntryError(ValidationError):
    """Raised when attempting to create a duplicate entry."""

    def __init__(
        self,
        resource_type: str,
        field: str,
        value: str
    ):
        super().__init__(
            message=f"Duplicate {resource_type} entry",
            detail=f"A {resource_type} with {field}='{value}' already exists"
        )


# =============================================================================
# Business Logic Exceptions
# =============================================================================

class BusinessLogicError(EVGException):
    """Base class for business logic violations."""

    def __init__(
        self,
        message: str = "Business logic error",
        detail: Optional[str] = None
    ):
        super().__init__(message=message, status_code=400, detail=detail)


class PackWindowClosedError(BusinessLogicError):
    """Raised when trying to open packs outside allowed time windows."""

    def __init__(self):
        super().__init__(
            message="Pack opening window is currently closed",
            detail="Wait for the next scheduled pack opening time"
        )


class ChallengeAlreadyCompletedError(BusinessLogicError):
    """Raised when trying to complete an already completed challenge."""

    def __init__(self, challenge_id: int):
        super().__init__(
            message="Challenge already completed",
            detail=f"Challenge {challenge_id} has already been completed"
        )


class NegativePointsError(BusinessLogicError):
    """Raised when an action would result in negative points."""

    def __init__(self):
        super().__init__(
            message="Points cannot be negative",
            detail="This action would result in a negative point balance"
        )


# =============================================================================
# Database Exceptions
# =============================================================================

class DatabaseError(EVGException):
    """Raised when a database operation fails."""

    def __init__(
        self,
        message: str = "Database operation failed",
        detail: Optional[str] = None
    ):
        super().__init__(message=message, status_code=500, detail=detail)


# =============================================================================
# Helper Functions
# =============================================================================

def format_exception_response(exc: EVGException) -> dict:
    """
    Format an exception into a JSON-serializable response.

    Args:
        exc: The exception to format

    Returns:
        Dictionary with error details suitable for API response

    Example:
        >>> exc = ParticipantNotFoundError(participant_id=123)
        >>> format_exception_response(exc)
        {
            "success": False,
            "error": "Participant with ID 123 not found",
            "detail": None,
            "status_code": 404
        }
    """
    return {
        "success": False,
        "error": exc.message,
        "detail": exc.detail,
        "status_code": exc.status_code
    }