dtlpy 1.113.10__py3-none-any.whl → 1.114.13__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.
- dtlpy/__init__.py +488 -488
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/__pycache__/__init__.cpython-38.pyc +0 -0
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +311 -311
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +296 -296
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +442 -442
- dtlpy/entities/dataset.py +1285 -1285
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +222 -222
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +641 -641
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +953 -953
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +694 -691
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +803 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +290 -267
- dtlpy/entities/pipeline.py +593 -593
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +499 -499
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +958 -958
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +491 -491
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +945 -940
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -348
- dtlpy/repositories/commands.py +158 -158
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +434 -406
- dtlpy/repositories/datasets.py +1291 -1291
- dtlpy/repositories/downloader.py +895 -895
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +266 -266
- dtlpy/repositories/executions.py +817 -817
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +238 -238
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +909 -915
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +877 -867
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +448 -448
- dtlpy/repositories/pipelines.py +642 -642
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +399 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1124 -1124
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +651 -651
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1782 -1782
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +264 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp +1 -1
- dtlpy-1.114.13.data/scripts/dlp.bat +2 -0
- {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp.py +128 -128
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/LICENSE +200 -200
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/METADATA +172 -172
- dtlpy-1.114.13.dist-info/RECORD +240 -0
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/WHEEL +1 -1
- tests/features/environment.py +551 -550
- dtlpy-1.113.10.data/scripts/dlp.bat +0 -2
- dtlpy-1.113.10.dist-info/RECORD +0 -244
- tests/assets/__init__.py +0 -0
- tests/assets/models_flow/__init__.py +0 -0
- tests/assets/models_flow/failedmain.py +0 -52
- tests/assets/models_flow/main.py +0 -62
- tests/assets/models_flow/main_model.py +0 -54
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/top_level.txt +0 -0
dtlpy/entities/paged_entities.py
CHANGED
|
@@ -1,268 +1,291 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import math
|
|
3
|
-
import time
|
|
4
|
-
import tqdm
|
|
5
|
-
import copy
|
|
6
|
-
import sys
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from
|
|
12
|
-
from ..
|
|
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
|
-
return
|
|
60
|
-
|
|
61
|
-
def
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if
|
|
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
|
-
self.
|
|
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
|
-
if
|
|
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
|
-
page_offset =
|
|
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
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import math
|
|
3
|
+
import time
|
|
4
|
+
import tqdm
|
|
5
|
+
import copy
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Optional, List, Any
|
|
8
|
+
|
|
9
|
+
import attr
|
|
10
|
+
|
|
11
|
+
from .filters import FiltersOperations, FiltersOrderByDirection, FiltersResource
|
|
12
|
+
from .. import miscellaneous
|
|
13
|
+
from ..services.api_client import ApiClient
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(name='dtlpy')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@attr.s
|
|
19
|
+
class PagedEntities:
|
|
20
|
+
"""
|
|
21
|
+
Pages object for efficient API pagination.
|
|
22
|
+
Defaults to offset-based pagination for compatibility with all operations.
|
|
23
|
+
Switches to keyset/cursor-based pagination (using 'id' as the cursor) during iteration for performance.
|
|
24
|
+
Falls back to offset-based pagination if keyset is not possible (e.g., custom sort).
|
|
25
|
+
"""
|
|
26
|
+
# api
|
|
27
|
+
_client_api: ApiClient = attr.ib(repr=False)
|
|
28
|
+
|
|
29
|
+
# params
|
|
30
|
+
page_offset: int = attr.ib()
|
|
31
|
+
page_size: int = attr.ib()
|
|
32
|
+
filters: Any = attr.ib()
|
|
33
|
+
items_repository: Any = attr.ib(repr=False)
|
|
34
|
+
has_next_page: bool = attr.ib(default=False)
|
|
35
|
+
total_pages_count: int = attr.ib(default=0)
|
|
36
|
+
items_count: int = attr.ib(default=0)
|
|
37
|
+
|
|
38
|
+
# hybrid pagination
|
|
39
|
+
use_id_based_paging: bool = attr.ib(default=False) # Default to False for offset-based pagination
|
|
40
|
+
last_seen_id: Optional[Any] = attr.ib(default=None)
|
|
41
|
+
|
|
42
|
+
# execution attribute
|
|
43
|
+
_service_id = attr.ib(default=None, repr=False)
|
|
44
|
+
_project_id = attr.ib(default=None, repr=False)
|
|
45
|
+
_list_function = attr.ib(default=None, repr=False)
|
|
46
|
+
|
|
47
|
+
# items list
|
|
48
|
+
items: List[Any] = attr.ib(default=miscellaneous.List(), repr=False)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _has_explicit_sort(flt) -> bool:
|
|
52
|
+
"""
|
|
53
|
+
Check if the filter has custom sort fields defined (not id/createdAt).
|
|
54
|
+
"""
|
|
55
|
+
prepared = flt.prepare() if flt else {}
|
|
56
|
+
sort_fields = list(prepared.get("sort", {}).keys())
|
|
57
|
+
if isinstance(sort_fields, list) and len(sort_fields) > 0:
|
|
58
|
+
return sort_fields[0] not in {"id", "createdAt"}
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
def _should_use_keyset_pagination(self) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Determine whether to use keyset pagination based on page offset and resource type.
|
|
64
|
+
Keyset pagination can only be used when page_offset is 0 (first page).
|
|
65
|
+
:param page_offset: The page offset to check
|
|
66
|
+
:return: True if keyset pagination should be used, False otherwise
|
|
67
|
+
"""
|
|
68
|
+
# Keyset pagination only works for page 0 (first page)
|
|
69
|
+
if self.page_offset != 0:
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
# can't use add to custom filter
|
|
73
|
+
if self.filters.custom_filter is not None:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
# Check if the resource supports keyset pagination
|
|
77
|
+
enable_id_based_paging = getattr(self.filters, "resource", None) in [
|
|
78
|
+
FiltersResource.ITEM,
|
|
79
|
+
FiltersResource.ANNOTATION,
|
|
80
|
+
FiltersResource.FEATURE,
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
if not enable_id_based_paging:
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
# Check if there's no explicit sort that would prevent keyset pagination
|
|
87
|
+
if self._has_explicit_sort(self.filters):
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
return True
|
|
91
|
+
|
|
92
|
+
def process_result(self, result: dict) -> List[Any]:
|
|
93
|
+
"""
|
|
94
|
+
Process the API result and update pagination state.
|
|
95
|
+
:param result: json object
|
|
96
|
+
:return: list of items
|
|
97
|
+
"""
|
|
98
|
+
# Only update page_offset if using offset-based pagination
|
|
99
|
+
if not self.use_id_based_paging and 'page_offset' in result:
|
|
100
|
+
self.page_offset = result['page_offset']
|
|
101
|
+
if 'page_size' in result:
|
|
102
|
+
self.page_size = result['page_size']
|
|
103
|
+
if 'hasNextPage' in result:
|
|
104
|
+
self.has_next_page = result['hasNextPage']
|
|
105
|
+
if 'totalItemsCount' in result:
|
|
106
|
+
self.items_count = result['totalItemsCount']
|
|
107
|
+
if 'totalPagesCount' in result:
|
|
108
|
+
self.total_pages_count = result['totalPagesCount']
|
|
109
|
+
if 'items' in result:
|
|
110
|
+
items = self.items_repository._build_entities_from_response(response_items=result['items'])
|
|
111
|
+
else:
|
|
112
|
+
items = miscellaneous.List(list())
|
|
113
|
+
return items
|
|
114
|
+
|
|
115
|
+
def __getitem__(self, y: int) -> List[Any]:
|
|
116
|
+
# If we're already on the requested page, return current items
|
|
117
|
+
if y == self.page_offset:
|
|
118
|
+
return self.items
|
|
119
|
+
# Otherwise, go to the requested page
|
|
120
|
+
self.go_to_page(y)
|
|
121
|
+
return self.items
|
|
122
|
+
|
|
123
|
+
def __len__(self) -> int:
|
|
124
|
+
return self.items_count
|
|
125
|
+
|
|
126
|
+
def __iter__(self):
|
|
127
|
+
# Use keyset/cursor-based pagination for iteration when possible
|
|
128
|
+
self.last_seen_id = None
|
|
129
|
+
self.page_offset = 0 # Start from the first page for iteration
|
|
130
|
+
self.use_id_based_paging = self._should_use_keyset_pagination()
|
|
131
|
+
self.has_next_page = True # Start with assumption that there are more pages
|
|
132
|
+
self.page_size = self.page_size or 100
|
|
133
|
+
pbar = tqdm.tqdm(total=self.items_count,
|
|
134
|
+
disable=self._client_api.verbose.disable_progress_bar_iterate_pages,
|
|
135
|
+
file=sys.stdout, desc="Iterate Pages")
|
|
136
|
+
|
|
137
|
+
# Get the first page
|
|
138
|
+
self.get_page()
|
|
139
|
+
if self.items:
|
|
140
|
+
yield self.items
|
|
141
|
+
pbar.update()
|
|
142
|
+
|
|
143
|
+
# Continue with next pages
|
|
144
|
+
while self.has_next_page:
|
|
145
|
+
if self.use_id_based_paging:
|
|
146
|
+
# For keyset pagination, just get the next page
|
|
147
|
+
self.page_offset = 0
|
|
148
|
+
self.get_page()
|
|
149
|
+
else:
|
|
150
|
+
# For offset pagination, increment the offset
|
|
151
|
+
self.page_offset += 1
|
|
152
|
+
self.get_page()
|
|
153
|
+
|
|
154
|
+
if not self.items:
|
|
155
|
+
break
|
|
156
|
+
yield self.items
|
|
157
|
+
pbar.update()
|
|
158
|
+
pbar.close()
|
|
159
|
+
|
|
160
|
+
def __reversed__(self):
|
|
161
|
+
# Force offset-based pagination for reverse iteration
|
|
162
|
+
self.use_id_based_paging = False
|
|
163
|
+
self.page_offset = self.total_pages_count - 1
|
|
164
|
+
while True:
|
|
165
|
+
self.get_page()
|
|
166
|
+
yield self.items
|
|
167
|
+
if self.page_offset == 0:
|
|
168
|
+
break
|
|
169
|
+
self.page_offset -= 1
|
|
170
|
+
|
|
171
|
+
def return_page(self, page_offset: Optional[int] = None, page_size: Optional[int] = None) -> List[Any]:
|
|
172
|
+
"""
|
|
173
|
+
Return a page of results using offset-based pagination by default.
|
|
174
|
+
Switches to keyset/cursor-based pagination when supported and beneficial.
|
|
175
|
+
:param page_offset: page offset (for offset-based)
|
|
176
|
+
:param page_size: page size
|
|
177
|
+
:return: list of items
|
|
178
|
+
"""
|
|
179
|
+
if page_size is not None:
|
|
180
|
+
self.page_size = page_size
|
|
181
|
+
if page_offset is not None:
|
|
182
|
+
self.page_offset = page_offset
|
|
183
|
+
|
|
184
|
+
if self.filters is None:
|
|
185
|
+
raise ValueError("Can't return page. Filters is empty")
|
|
186
|
+
self.filters.page_size = self.page_size
|
|
187
|
+
self.filters.page = self.page_offset
|
|
188
|
+
req = copy.deepcopy(self.filters)
|
|
189
|
+
|
|
190
|
+
# Determine pagination method based on page offset and resource type
|
|
191
|
+
self.use_id_based_paging = self._should_use_keyset_pagination()
|
|
192
|
+
|
|
193
|
+
if self.use_id_based_paging:
|
|
194
|
+
# Use keyset/cursor-based pagination
|
|
195
|
+
prepared = req.prepare()
|
|
196
|
+
sort_spec = prepared.get("sort", {})
|
|
197
|
+
order = next(iter(sort_spec.values()), None)
|
|
198
|
+
if order is None:
|
|
199
|
+
order = FiltersOrderByDirection.ASCENDING
|
|
200
|
+
if order == FiltersOrderByDirection.DESCENDING:
|
|
201
|
+
operator_value = FiltersOperations.LESS_THAN
|
|
202
|
+
else:
|
|
203
|
+
operator_value = FiltersOperations.GREATER_THAN
|
|
204
|
+
|
|
205
|
+
req.sort_by(field="id", value=order)
|
|
206
|
+
req.page = 0 # always fetch from the start for keyset
|
|
207
|
+
# Only add last_seen_id filter if we're not explicitly requesting page 0
|
|
208
|
+
if self.last_seen_id:
|
|
209
|
+
req.add(
|
|
210
|
+
field="id",
|
|
211
|
+
values=self.last_seen_id,
|
|
212
|
+
operator=operator_value,
|
|
213
|
+
method=FiltersOperations.AND,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Fetch data
|
|
217
|
+
if self._list_function is None:
|
|
218
|
+
result = self.items_repository._list(filters=req)
|
|
219
|
+
else:
|
|
220
|
+
result = self._list_function(filters=req)
|
|
221
|
+
|
|
222
|
+
items = self.process_result(result)
|
|
223
|
+
|
|
224
|
+
# Update last_seen_id for keyset
|
|
225
|
+
if self.use_id_based_paging and items and hasattr(items[-1], "id"):
|
|
226
|
+
self.last_seen_id = items[-1].id
|
|
227
|
+
elif self.use_id_based_paging and not items:
|
|
228
|
+
self.last_seen_id = None
|
|
229
|
+
return items
|
|
230
|
+
|
|
231
|
+
def get_page(self, page_offset: Optional[int] = None, page_size: Optional[int] = None) -> None:
|
|
232
|
+
"""
|
|
233
|
+
Get a page of results and update self.items.
|
|
234
|
+
:param page_offset: page offset (for offset-based)
|
|
235
|
+
:param page_size: page size
|
|
236
|
+
"""
|
|
237
|
+
items = self.return_page(page_offset=page_offset, page_size=page_size)
|
|
238
|
+
self.items = items
|
|
239
|
+
|
|
240
|
+
def next_page(self) -> None:
|
|
241
|
+
"""
|
|
242
|
+
Brings the next page of items from host.
|
|
243
|
+
"""
|
|
244
|
+
if self.use_id_based_paging:
|
|
245
|
+
# For keyset pagination, just get the next page
|
|
246
|
+
self.get_page()
|
|
247
|
+
else:
|
|
248
|
+
# For offset pagination, increment the offset
|
|
249
|
+
self.page_offset += 1
|
|
250
|
+
self.get_page()
|
|
251
|
+
|
|
252
|
+
def prev_page(self) -> None:
|
|
253
|
+
"""
|
|
254
|
+
Brings the previous page of items from host.
|
|
255
|
+
Only works with offset-based pagination.
|
|
256
|
+
"""
|
|
257
|
+
if self.use_id_based_paging:
|
|
258
|
+
raise NotImplementedError("prev_page is not supported for keyset pagination.")
|
|
259
|
+
self.page_offset -= 1
|
|
260
|
+
self.get_page()
|
|
261
|
+
|
|
262
|
+
def go_to_page(self, page: int = 0) -> None:
|
|
263
|
+
"""
|
|
264
|
+
Brings specified page of items from host.
|
|
265
|
+
For page 0, uses keyset pagination if supported.
|
|
266
|
+
For other pages, uses offset-based pagination.
|
|
267
|
+
:param page: page number
|
|
268
|
+
"""
|
|
269
|
+
# Reset last_seen_id when going to page 0 to ensure we get all items
|
|
270
|
+
if page == 0:
|
|
271
|
+
self.last_seen_id = None
|
|
272
|
+
self.page_offset = page
|
|
273
|
+
self.get_page()
|
|
274
|
+
|
|
275
|
+
def all(self):
|
|
276
|
+
"""
|
|
277
|
+
Iterate over all items in all pages efficiently.
|
|
278
|
+
Uses the iterator implementation (__iter__).
|
|
279
|
+
"""
|
|
280
|
+
for items in self:
|
|
281
|
+
for item in items:
|
|
282
|
+
yield item
|
|
283
|
+
|
|
284
|
+
########
|
|
285
|
+
# misc #
|
|
286
|
+
########
|
|
287
|
+
def print(self, columns=None):
|
|
288
|
+
self.items.print(columns=columns)
|
|
289
|
+
|
|
290
|
+
def to_df(self, columns=None):
|
|
268
291
|
return self.items.to_df(columns=columns)
|