enkryptai-sdk 1.0.24__py3-none-any.whl → 1.0.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- enkryptai_sdk/__init__.py +63 -5
- enkryptai_sdk/dto/__init__.py +9 -0
- enkryptai_sdk/dto/datasets.py +66 -16
- enkryptai_sdk/dto/models.py +81 -17
- enkryptai_sdk/dto/red_team.py +396 -82
- enkryptai_sdk/red_team.py +270 -58
- enkryptai_sdk/response.py +271 -0
- enkryptai_sdk/utils/__init__.py +29 -0
- enkryptai_sdk/utils/pagination.py +384 -0
- {enkryptai_sdk-1.0.24.dist-info → enkryptai_sdk-1.0.26.dist-info}/METADATA +1 -1
- {enkryptai_sdk-1.0.24.dist-info → enkryptai_sdk-1.0.26.dist-info}/RECORD +14 -12
- {enkryptai_sdk-1.0.24.dist-info → enkryptai_sdk-1.0.26.dist-info}/WHEEL +0 -0
- {enkryptai_sdk-1.0.24.dist-info → enkryptai_sdk-1.0.26.dist-info}/licenses/LICENSE +0 -0
- {enkryptai_sdk-1.0.24.dist-info → enkryptai_sdk-1.0.26.dist-info}/top_level.txt +0 -0
enkryptai_sdk/response.py
CHANGED
|
@@ -1,3 +1,238 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import Dict, Any, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PaginationInfo:
|
|
6
|
+
"""
|
|
7
|
+
A class to handle pagination information and calculations.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, page: int = 1, per_page: int = 10, total_count: int = 0):
|
|
11
|
+
"""
|
|
12
|
+
Initialize pagination information.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
page (int): Current page number (1-based)
|
|
16
|
+
per_page (int): Number of items per page
|
|
17
|
+
total_count (int): Total number of items
|
|
18
|
+
"""
|
|
19
|
+
self.page = max(1, page)
|
|
20
|
+
self.per_page = max(1, min(100, per_page)) # Ensure per_page is between 1 and 100
|
|
21
|
+
self.total_count = max(0, total_count)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def total_pages(self) -> int:
|
|
25
|
+
"""Calculate total number of pages."""
|
|
26
|
+
if self.total_count == 0:
|
|
27
|
+
return 0
|
|
28
|
+
return math.ceil(self.total_count / self.per_page)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def has_next(self) -> bool:
|
|
32
|
+
"""Check if there's a next page."""
|
|
33
|
+
return self.page < self.total_pages
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def has_previous(self) -> bool:
|
|
37
|
+
"""Check if there's a previous page."""
|
|
38
|
+
return self.page > 1
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def offset(self) -> int:
|
|
42
|
+
"""Calculate the offset for database queries."""
|
|
43
|
+
return (self.page - 1) * self.per_page
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def limit(self) -> int:
|
|
47
|
+
"""Get the limit for database queries."""
|
|
48
|
+
return self.per_page
|
|
49
|
+
|
|
50
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
51
|
+
"""Convert pagination info to dictionary."""
|
|
52
|
+
return {
|
|
53
|
+
"page": self.page,
|
|
54
|
+
"per_page": self.per_page,
|
|
55
|
+
"total_count": self.total_count,
|
|
56
|
+
"total_pages": self.total_pages,
|
|
57
|
+
"has_next": self.has_next,
|
|
58
|
+
"has_previous": self.has_previous
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_query_params(cls, query_params: Dict[str, Any], default_per_page: int = 10) -> "PaginationInfo":
|
|
63
|
+
"""
|
|
64
|
+
Create PaginationInfo from query parameters.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
query_params (Dict[str, Any]): Dictionary containing query parameters
|
|
68
|
+
default_per_page (int): Default items per page
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
PaginationInfo: Pagination information object
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
page = int(query_params.get("page", 1))
|
|
75
|
+
per_page = int(query_params.get("per_page", default_per_page))
|
|
76
|
+
except (ValueError, TypeError):
|
|
77
|
+
page = 1
|
|
78
|
+
per_page = default_per_page
|
|
79
|
+
|
|
80
|
+
# Validate pagination parameters
|
|
81
|
+
if page < 1:
|
|
82
|
+
page = 1
|
|
83
|
+
if per_page < 1 or per_page > 100:
|
|
84
|
+
per_page = min(max(1, per_page), 100)
|
|
85
|
+
|
|
86
|
+
return cls(page=page, per_page=per_page)
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def validate_params(cls, page: Union[str, int], per_page: Union[str, int]) -> tuple[int, int]:
|
|
90
|
+
"""
|
|
91
|
+
Validate pagination parameters and return validated values.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
page: Page number (can be string or int)
|
|
95
|
+
per_page: Items per page (can be string or int)
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
tuple[int, int]: Validated (page, per_page) values
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
ValueError: If parameters are invalid
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
page_num = int(page) if page is not None else 1
|
|
105
|
+
per_page_num = int(per_page) if per_page is not None else 10
|
|
106
|
+
except (ValueError, TypeError):
|
|
107
|
+
raise ValueError("Page and per_page must be valid integers")
|
|
108
|
+
|
|
109
|
+
if page_num < 1:
|
|
110
|
+
raise ValueError("Page must be >= 1")
|
|
111
|
+
if per_page_num < 1 or per_page_num > 100:
|
|
112
|
+
raise ValueError("Per_page must be between 1 and 100")
|
|
113
|
+
|
|
114
|
+
return page_num, per_page_num
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class PaginatedResponse(dict):
|
|
118
|
+
"""
|
|
119
|
+
A wrapper class for paginated API responses that provides pagination information
|
|
120
|
+
and maintains backward compatibility with dictionary access.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, data: List[Any], pagination: PaginationInfo, **kwargs):
|
|
124
|
+
"""
|
|
125
|
+
Initialize the PaginatedResponse object.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
data (List[Any]): List of items for the current page
|
|
129
|
+
pagination (PaginationInfo): Pagination information
|
|
130
|
+
**kwargs: Additional response data
|
|
131
|
+
"""
|
|
132
|
+
response_data = {
|
|
133
|
+
"data": data,
|
|
134
|
+
"pagination": pagination.to_dict(),
|
|
135
|
+
**kwargs
|
|
136
|
+
}
|
|
137
|
+
super().__init__(response_data)
|
|
138
|
+
self._data = response_data
|
|
139
|
+
self._pagination = pagination
|
|
140
|
+
|
|
141
|
+
def get_data(self) -> List[Any]:
|
|
142
|
+
"""Get the data items for the current page."""
|
|
143
|
+
return self._data.get("data", [])
|
|
144
|
+
|
|
145
|
+
def get_pagination(self) -> Dict[str, Any]:
|
|
146
|
+
"""Get pagination information."""
|
|
147
|
+
return self._data.get("pagination", {})
|
|
148
|
+
|
|
149
|
+
def get_page(self) -> int:
|
|
150
|
+
"""Get current page number."""
|
|
151
|
+
return self._pagination.page
|
|
152
|
+
|
|
153
|
+
def get_per_page(self) -> int:
|
|
154
|
+
"""Get items per page."""
|
|
155
|
+
return self._pagination.per_page
|
|
156
|
+
|
|
157
|
+
def get_total_count(self) -> int:
|
|
158
|
+
"""Get total number of items."""
|
|
159
|
+
return self._pagination.total_count
|
|
160
|
+
|
|
161
|
+
def get_total_pages(self) -> int:
|
|
162
|
+
"""Get total number of pages."""
|
|
163
|
+
return self._pagination.total_pages
|
|
164
|
+
|
|
165
|
+
def has_next_page(self) -> bool:
|
|
166
|
+
"""Check if there's a next page."""
|
|
167
|
+
return self._pagination.has_next
|
|
168
|
+
|
|
169
|
+
def has_previous_page(self) -> bool:
|
|
170
|
+
"""Check if there's a previous page."""
|
|
171
|
+
return self._pagination.has_previous
|
|
172
|
+
|
|
173
|
+
def get_next_page_url(self, base_url: str, **query_params) -> Optional[str]:
|
|
174
|
+
"""
|
|
175
|
+
Generate URL for the next page.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
base_url (str): Base URL for the endpoint
|
|
179
|
+
**query_params: Additional query parameters to include
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Optional[str]: Next page URL or None if no next page
|
|
183
|
+
"""
|
|
184
|
+
if not self.has_next_page():
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
params = {**query_params, "page": self.get_page() + 1, "per_page": self.get_per_page()}
|
|
188
|
+
query_string = "&".join([f"{k}={v}" for k, v in params.items()])
|
|
189
|
+
return f"{base_url}?{query_string}"
|
|
190
|
+
|
|
191
|
+
def get_previous_page_url(self, base_url: str, **query_params) -> Optional[str]:
|
|
192
|
+
"""
|
|
193
|
+
Generate URL for the previous page.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
base_url (str): Base URL for the endpoint
|
|
197
|
+
**query_params: Additional query parameters to include
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Optional[str]: Previous page URL or None if no previous page
|
|
201
|
+
"""
|
|
202
|
+
if not self.has_previous_page():
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
params = {**query_params, "page": self.get_page() - 1, "per_page": self.get_per_page()}
|
|
206
|
+
query_string = "&".join([f"{k}={v}" for k, v in params.items()])
|
|
207
|
+
return f"{base_url}?{query_string}"
|
|
208
|
+
|
|
209
|
+
def get_page_urls(self, base_url: str, **query_params) -> Dict[str, Optional[str]]:
|
|
210
|
+
"""
|
|
211
|
+
Generate URLs for all pagination actions.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
base_url (str): Base URL for the endpoint
|
|
215
|
+
**query_params: Additional query parameters to include
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Dict[str, Optional[str]]: Dictionary with pagination URLs
|
|
219
|
+
"""
|
|
220
|
+
return {
|
|
221
|
+
"first": f"{base_url}?{self._build_query_string(1, **query_params)}",
|
|
222
|
+
"previous": self.get_previous_page_url(base_url, **query_params),
|
|
223
|
+
"current": f"{base_url}?{self._build_query_string(self.get_page(), **query_params)}",
|
|
224
|
+
"next": self.get_next_page_url(base_url, **query_params),
|
|
225
|
+
"last": f"{base_url}?{self._build_query_string(self.get_total_pages(), **query_params)}" if self.get_total_pages() > 0 else None
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
def _build_query_string(self, page: int, **query_params) -> str:
|
|
229
|
+
"""Build query string for a specific page."""
|
|
230
|
+
params = {**query_params, "page": page, "per_page": self.get_per_page()}
|
|
231
|
+
return "&".join([f"{k}={v}" for k, v in params.items()])
|
|
232
|
+
|
|
233
|
+
def __str__(self) -> str:
|
|
234
|
+
"""String representation of the paginated response."""
|
|
235
|
+
return f"PaginatedResponse(page={self.get_page()}, per_page={self.get_per_page()}, total={self.get_total_count()}, items={len(self.get_data())})"
|
|
1
236
|
|
|
2
237
|
|
|
3
238
|
class GuardrailsResponse(dict):
|
|
@@ -100,7 +335,25 @@ class GuardrailsResponse(dict):
|
|
|
100
335
|
|
|
101
336
|
return f"Response Status: {status}\n{violation_str}"
|
|
102
337
|
|
|
338
|
+
def get_pagination(self) -> Optional[Dict[str, Any]]:
|
|
339
|
+
"""
|
|
340
|
+
Get pagination information if available.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Optional[Dict[str, Any]]: Pagination data or None if not available
|
|
344
|
+
"""
|
|
345
|
+
return self._data.get("pagination")
|
|
103
346
|
|
|
347
|
+
def is_paginated(self) -> bool:
|
|
348
|
+
"""
|
|
349
|
+
Check if the response contains pagination information.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
bool: True if response is paginated, False otherwise
|
|
353
|
+
"""
|
|
354
|
+
return "pagination" in self._data
|
|
355
|
+
|
|
356
|
+
|
|
104
357
|
class PIIResponse(dict):
|
|
105
358
|
"""
|
|
106
359
|
A wrapper class for Enkrypt AI PII API responses that provides additional functionality
|
|
@@ -132,4 +385,22 @@ class PIIResponse(dict):
|
|
|
132
385
|
"""
|
|
133
386
|
return self._data.get("key", "")
|
|
134
387
|
|
|
388
|
+
def get_pagination(self) -> Optional[Dict[str, Any]]:
|
|
389
|
+
"""
|
|
390
|
+
Get pagination information if available.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Optional[Dict[str, Any]]: Pagination data or None if not available
|
|
394
|
+
"""
|
|
395
|
+
return self._data.get("pagination")
|
|
396
|
+
|
|
397
|
+
def is_paginated(self) -> bool:
|
|
398
|
+
"""
|
|
399
|
+
Check if the response contains pagination information.
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
bool: True if response is paginated, False otherwise
|
|
403
|
+
"""
|
|
404
|
+
return "pagination" in self._data
|
|
405
|
+
|
|
135
406
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Utils module for Enkrypt AI SDK
|
|
2
|
+
|
|
3
|
+
from .pagination import (
|
|
4
|
+
PaginationInfo,
|
|
5
|
+
PaginatedResponse,
|
|
6
|
+
parse_pagination_params,
|
|
7
|
+
build_pagination_url,
|
|
8
|
+
create_paginated_response,
|
|
9
|
+
validate_pagination_params,
|
|
10
|
+
get_pagination_metadata,
|
|
11
|
+
calculate_page_info,
|
|
12
|
+
create_pagination_links,
|
|
13
|
+
apply_pagination_to_list,
|
|
14
|
+
format_pagination_response
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"PaginationInfo",
|
|
19
|
+
"PaginatedResponse",
|
|
20
|
+
"parse_pagination_params",
|
|
21
|
+
"build_pagination_url",
|
|
22
|
+
"create_paginated_response",
|
|
23
|
+
"validate_pagination_params",
|
|
24
|
+
"get_pagination_metadata",
|
|
25
|
+
"calculate_page_info",
|
|
26
|
+
"create_pagination_links",
|
|
27
|
+
"apply_pagination_to_list",
|
|
28
|
+
"format_pagination_response"
|
|
29
|
+
]
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pagination utilities for Enkrypt AI SDK.
|
|
3
|
+
|
|
4
|
+
This module provides helper functions for handling pagination in API requests and responses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any, List, Optional, Union, Tuple
|
|
8
|
+
from urllib.parse import urlencode, parse_qs, urlparse, urlunparse
|
|
9
|
+
from ..response import PaginationInfo, PaginatedResponse
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_pagination_params(
|
|
13
|
+
query_string: str = "",
|
|
14
|
+
default_page: int = 1,
|
|
15
|
+
default_per_page: int = 10
|
|
16
|
+
) -> PaginationInfo:
|
|
17
|
+
"""
|
|
18
|
+
Parse pagination parameters from a query string.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
query_string (str): URL query string (e.g., "page=2&per_page=20")
|
|
22
|
+
default_page (int): Default page number if not specified
|
|
23
|
+
default_per_page (int): Default items per page if not specified
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
PaginationInfo: Parsed pagination information
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> parse_pagination_params("page=2&per_page=20")
|
|
30
|
+
PaginationInfo(page=2, per_page=20, total_count=0)
|
|
31
|
+
"""
|
|
32
|
+
if not query_string:
|
|
33
|
+
return PaginationInfo(default_page, default_per_page)
|
|
34
|
+
|
|
35
|
+
# Parse query string
|
|
36
|
+
params = parse_qs(query_string)
|
|
37
|
+
|
|
38
|
+
# Extract pagination parameters
|
|
39
|
+
page = params.get("page", [str(default_page)])[0]
|
|
40
|
+
per_page = params.get("per_page", [str(default_per_page)])[0]
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
page_num = int(page)
|
|
44
|
+
per_page_num = int(per_page)
|
|
45
|
+
except (ValueError, TypeError):
|
|
46
|
+
page_num = default_page
|
|
47
|
+
per_page_num = default_per_page
|
|
48
|
+
|
|
49
|
+
# Validate and create PaginationInfo
|
|
50
|
+
return PaginationInfo(
|
|
51
|
+
page=max(1, page_num),
|
|
52
|
+
per_page=max(1, min(100, per_page_num)),
|
|
53
|
+
total_count=0
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def build_pagination_url(
|
|
58
|
+
base_url: str,
|
|
59
|
+
page: int,
|
|
60
|
+
per_page: int,
|
|
61
|
+
**additional_params
|
|
62
|
+
) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Build a URL with pagination parameters.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
base_url (str): Base URL without query parameters
|
|
68
|
+
page (int): Page number
|
|
69
|
+
per_page (int): Items per page
|
|
70
|
+
**additional_params: Additional query parameters
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
str: Complete URL with pagination parameters
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> build_pagination_url("https://api.example.com/users", 2, 20, status="active")
|
|
77
|
+
'https://api.example.com/users?page=2&per_page=20&status=active'
|
|
78
|
+
"""
|
|
79
|
+
params = {
|
|
80
|
+
"page": page,
|
|
81
|
+
"per_page": per_page,
|
|
82
|
+
**additional_params
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Filter out None values
|
|
86
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
87
|
+
|
|
88
|
+
query_string = urlencode(params)
|
|
89
|
+
return f"{base_url}?{query_string}" if query_string else base_url
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def create_paginated_response(
|
|
93
|
+
data: List[Any],
|
|
94
|
+
total_count: int,
|
|
95
|
+
page: int = 1,
|
|
96
|
+
per_page: int = 10,
|
|
97
|
+
**additional_data
|
|
98
|
+
) -> PaginatedResponse:
|
|
99
|
+
"""
|
|
100
|
+
Create a paginated response object.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
data (List[Any]): List of items for the current page
|
|
104
|
+
total_count (int): Total number of items across all pages
|
|
105
|
+
page (int): Current page number
|
|
106
|
+
per_page (int): Items per page
|
|
107
|
+
**additional_data: Additional data to include in the response
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
PaginatedResponse: Paginated response object
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
>>> users = [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]
|
|
114
|
+
>>> response = create_paginated_response(users, 100, 1, 10)
|
|
115
|
+
>>> response.get_page()
|
|
116
|
+
1
|
|
117
|
+
>>> response.get_total_count()
|
|
118
|
+
100
|
|
119
|
+
"""
|
|
120
|
+
pagination = PaginationInfo(page, per_page, total_count)
|
|
121
|
+
return PaginatedResponse(data, pagination, **additional_data)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def validate_pagination_params(
|
|
125
|
+
page: Union[str, int, None],
|
|
126
|
+
per_page: Union[str, int, None]
|
|
127
|
+
) -> Tuple[int, int]:
|
|
128
|
+
"""
|
|
129
|
+
Validate pagination parameters and return validated values.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
page: Page number (can be string, int, or None)
|
|
133
|
+
per_page: Items per page (can be string, int, or None)
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Tuple[int, int]: Validated (page, per_page) values
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
ValueError: If parameters are invalid
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> validate_pagination_params("2", "20")
|
|
143
|
+
(2, 20)
|
|
144
|
+
>>> validate_pagination_params(None, None)
|
|
145
|
+
(1, 10)
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
page_num = int(page) if page is not None else 1
|
|
149
|
+
per_page_num = int(per_page) if per_page is not None else 10
|
|
150
|
+
except (ValueError, TypeError):
|
|
151
|
+
raise ValueError("Page and per_page must be valid integers")
|
|
152
|
+
|
|
153
|
+
if page_num < 1:
|
|
154
|
+
raise ValueError("Page must be >= 1")
|
|
155
|
+
if per_page_num < 1 or per_page_num > 100:
|
|
156
|
+
raise ValueError("Per_page must be between 1 and 100")
|
|
157
|
+
|
|
158
|
+
return page_num, per_page_num
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_pagination_metadata(
|
|
162
|
+
total_count: int,
|
|
163
|
+
page: int,
|
|
164
|
+
per_page: int
|
|
165
|
+
) -> Dict[str, Any]:
|
|
166
|
+
"""
|
|
167
|
+
Generate pagination metadata for API responses.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
total_count (int): Total number of items
|
|
171
|
+
page (int): Current page number
|
|
172
|
+
per_page (int): Items per page
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Dict[str, Any]: Pagination metadata dictionary
|
|
176
|
+
|
|
177
|
+
Example:
|
|
178
|
+
>>> get_pagination_metadata(100, 2, 20)
|
|
179
|
+
{
|
|
180
|
+
'page': 2,
|
|
181
|
+
'per_page': 20,
|
|
182
|
+
'total_count': 100,
|
|
183
|
+
'total_pages': 5,
|
|
184
|
+
'has_next': True,
|
|
185
|
+
'has_previous': True
|
|
186
|
+
}
|
|
187
|
+
"""
|
|
188
|
+
pagination = PaginationInfo(page, per_page, total_count)
|
|
189
|
+
return pagination.to_dict()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def calculate_page_info(
|
|
193
|
+
total_count: int,
|
|
194
|
+
page: int,
|
|
195
|
+
per_page: int
|
|
196
|
+
) -> Dict[str, Any]:
|
|
197
|
+
"""
|
|
198
|
+
Calculate detailed pagination information.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
total_count (int): Total number of items
|
|
202
|
+
page (int): Current page number
|
|
203
|
+
per_page (int): Items per page
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Dict[str, Any]: Detailed pagination information
|
|
207
|
+
|
|
208
|
+
Example:
|
|
209
|
+
>>> calculate_page_info(100, 2, 20)
|
|
210
|
+
{
|
|
211
|
+
'current_page': 2,
|
|
212
|
+
'per_page': 20,
|
|
213
|
+
'total_count': 100,
|
|
214
|
+
'total_pages': 5,
|
|
215
|
+
'has_next': True,
|
|
216
|
+
'has_previous': True,
|
|
217
|
+
'offset': 20,
|
|
218
|
+
'limit': 20,
|
|
219
|
+
'start_item': 21,
|
|
220
|
+
'end_item': 40
|
|
221
|
+
}
|
|
222
|
+
"""
|
|
223
|
+
pagination = PaginationInfo(page, per_page, total_count)
|
|
224
|
+
|
|
225
|
+
start_item = (page - 1) * per_page + 1
|
|
226
|
+
end_item = min(page * per_page, total_count)
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
'current_page': pagination.page,
|
|
230
|
+
'per_page': pagination.per_page,
|
|
231
|
+
'total_count': pagination.total_count,
|
|
232
|
+
'total_pages': pagination.total_pages,
|
|
233
|
+
'has_next': pagination.has_next,
|
|
234
|
+
'has_previous': pagination.has_previous,
|
|
235
|
+
'offset': pagination.offset,
|
|
236
|
+
'limit': pagination.limit,
|
|
237
|
+
'start_item': start_item if total_count > 0 else 0,
|
|
238
|
+
'end_item': end_item if total_count > 0 else 0
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def create_pagination_links(
|
|
243
|
+
base_url: str,
|
|
244
|
+
current_page: int,
|
|
245
|
+
total_pages: int,
|
|
246
|
+
per_page: int,
|
|
247
|
+
**additional_params
|
|
248
|
+
) -> Dict[str, Optional[str]]:
|
|
249
|
+
"""
|
|
250
|
+
Create pagination links for navigation.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
base_url (str): Base URL for the endpoint
|
|
254
|
+
current_page (int): Current page number
|
|
255
|
+
total_pages (int): Total number of pages
|
|
256
|
+
per_page (int): Items per page
|
|
257
|
+
**additional_params: Additional query parameters
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Dict[str, Optional[str]]: Dictionary with pagination links
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
>>> links = create_pagination_links("https://api.example.com/users", 2, 5, 20)
|
|
264
|
+
>>> links
|
|
265
|
+
{
|
|
266
|
+
'first': 'https://api.example.com/users?page=1&per_page=20',
|
|
267
|
+
'previous': 'https://api.example.com/users?page=1&per_page=20',
|
|
268
|
+
'current': 'https://api.example.com/users?page=2&per_page=20',
|
|
269
|
+
'next': 'https://api.example.com/users?page=3&per_page=20',
|
|
270
|
+
'last': 'https://api.example.com/users?page=5&per_page=20'
|
|
271
|
+
}
|
|
272
|
+
"""
|
|
273
|
+
links = {}
|
|
274
|
+
|
|
275
|
+
# First page
|
|
276
|
+
links['first'] = build_pagination_url(base_url, 1, per_page, **additional_params)
|
|
277
|
+
|
|
278
|
+
# Previous page
|
|
279
|
+
if current_page > 1:
|
|
280
|
+
links['previous'] = build_pagination_url(base_url, current_page - 1, per_page, **additional_params)
|
|
281
|
+
else:
|
|
282
|
+
links['previous'] = None
|
|
283
|
+
|
|
284
|
+
# Current page
|
|
285
|
+
links['current'] = build_pagination_url(base_url, current_page, per_page, **additional_params)
|
|
286
|
+
|
|
287
|
+
# Next page
|
|
288
|
+
if current_page < total_pages:
|
|
289
|
+
links['next'] = build_pagination_url(base_url, current_page + 1, per_page, **additional_params)
|
|
290
|
+
else:
|
|
291
|
+
links['next'] = None
|
|
292
|
+
|
|
293
|
+
# Last page
|
|
294
|
+
if total_pages > 0:
|
|
295
|
+
links['last'] = build_pagination_url(base_url, total_pages, per_page, **additional_params)
|
|
296
|
+
else:
|
|
297
|
+
links['last'] = None
|
|
298
|
+
|
|
299
|
+
return links
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def apply_pagination_to_list(
|
|
303
|
+
data: List[Any],
|
|
304
|
+
page: int,
|
|
305
|
+
per_page: int
|
|
306
|
+
) -> Tuple[List[Any], int]:
|
|
307
|
+
"""
|
|
308
|
+
Apply pagination to a list of data.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
data (List[Any]): Complete list of data
|
|
312
|
+
page (int): Page number (1-based)
|
|
313
|
+
per_page (int): Items per page
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Tuple[List[Any], int]: (paginated_data, total_count)
|
|
317
|
+
|
|
318
|
+
Example:
|
|
319
|
+
>>> all_users = [{"id": i, "name": f"User{i}"} for i in range(1, 101)]
|
|
320
|
+
>>> page_data, total = apply_pagination_to_list(all_users, 2, 20)
|
|
321
|
+
>>> len(page_data)
|
|
322
|
+
20
|
|
323
|
+
>>> total
|
|
324
|
+
100
|
|
325
|
+
"""
|
|
326
|
+
total_count = len(data)
|
|
327
|
+
start_index = (page - 1) * per_page
|
|
328
|
+
end_index = start_index + per_page
|
|
329
|
+
|
|
330
|
+
paginated_data = data[start_index:end_index]
|
|
331
|
+
return paginated_data, total_count
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def format_pagination_response(
|
|
335
|
+
data: List[Any],
|
|
336
|
+
total_count: int,
|
|
337
|
+
page: int,
|
|
338
|
+
per_page: int,
|
|
339
|
+
include_links: bool = True,
|
|
340
|
+
base_url: str = "",
|
|
341
|
+
**additional_data
|
|
342
|
+
) -> Dict[str, Any]:
|
|
343
|
+
"""
|
|
344
|
+
Format a complete paginated response.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
data (List[Any]): List of items for the current page
|
|
348
|
+
total_count (int): Total number of items
|
|
349
|
+
page (int): Current page number
|
|
350
|
+
per_page (int): Items per page
|
|
351
|
+
include_links (bool): Whether to include pagination links
|
|
352
|
+
base_url (str): Base URL for generating pagination links
|
|
353
|
+
**additional_data: Additional data to include
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Dict[str, Any]: Formatted paginated response
|
|
357
|
+
|
|
358
|
+
Example:
|
|
359
|
+
>>> response = format_pagination_response(
|
|
360
|
+
... [{"id": 1, "name": "John"}],
|
|
361
|
+
... 100, 1, 10,
|
|
362
|
+
... base_url="https://api.example.com/users"
|
|
363
|
+
... )
|
|
364
|
+
>>> response.keys()
|
|
365
|
+
dict_keys(['data', 'pagination', 'links'])
|
|
366
|
+
"""
|
|
367
|
+
# Create pagination metadata
|
|
368
|
+
pagination = get_pagination_metadata(total_count, page, per_page)
|
|
369
|
+
|
|
370
|
+
# Build response
|
|
371
|
+
response = {
|
|
372
|
+
"data": data,
|
|
373
|
+
"pagination": pagination,
|
|
374
|
+
**additional_data
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
# Add pagination links if requested
|
|
378
|
+
if include_links and base_url:
|
|
379
|
+
links = create_pagination_links(
|
|
380
|
+
base_url, page, pagination['total_pages'], per_page
|
|
381
|
+
)
|
|
382
|
+
response["links"] = links
|
|
383
|
+
|
|
384
|
+
return response
|