dtlpy 1.115.44__py3-none-any.whl → 1.117.6__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 +491 -491
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- 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 +347 -347
- 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 +292 -292
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -449
- dtlpy/entities/dataset.py +1299 -1299
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -235
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +152 -145
- dtlpy/entities/filters.py +798 -798
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +975 -959
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +684 -684
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +810 -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 +299 -299
- dtlpy/entities/pipeline.py +624 -624
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +505 -505
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +974 -963
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +495 -495
- 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 +1287 -1230
- 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 -332
- dtlpy/repositories/commands.py +152 -152
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -439
- dtlpy/repositories/datasets.py +1585 -1504
- dtlpy/repositories/downloader.py +1157 -923
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -482
- dtlpy/repositories/executions.py +815 -815
- dtlpy/repositories/feature_sets.py +256 -226
- dtlpy/repositories/features.py +255 -255
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -912
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -1000
- 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 +451 -451
- dtlpy/repositories/pipelines.py +640 -640
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +429 -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 +1477 -1477
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +661 -661
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1786 -1785
- 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 +285 -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.115.44.data → dtlpy-1.117.6.data}/scripts/dlp +1 -1
- dtlpy-1.117.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/METADATA +186 -186
- dtlpy-1.117.6.dist-info/RECORD +239 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
- dtlpy-1.115.44.dist-info/RECORD +0 -240
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/top_level.txt +0 -0
dtlpy/repositories/recipes.py
CHANGED
|
@@ -1,399 +1,429 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import traceback
|
|
3
|
-
import urllib.parse
|
|
4
|
-
|
|
5
|
-
from .. import entities, miscellaneous, repositories, exceptions, _api_reference
|
|
6
|
-
from ..services.api_client import ApiClient
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger(name='dtlpy')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Recipes:
|
|
12
|
-
"""
|
|
13
|
-
Recipes Repository
|
|
14
|
-
|
|
15
|
-
The Recipes class allows you to manage recipes and their properties.
|
|
16
|
-
For more information on Recipes, see our `documentation <https://dataloop.ai/docs/ontology>`_ and `developers' documentation <https://developers.dataloop.ai/tutorials/recipe_and_ontology/recipe/chapter/>`_.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
def __init__(self,
|
|
20
|
-
client_api: ApiClient,
|
|
21
|
-
dataset: entities.Dataset = None,
|
|
22
|
-
project: entities.Project = None,
|
|
23
|
-
project_id: str = None):
|
|
24
|
-
self._client_api = client_api
|
|
25
|
-
self._dataset = dataset
|
|
26
|
-
self._project = project
|
|
27
|
-
self._project_id = project_id
|
|
28
|
-
if project_id is None and project is not None:
|
|
29
|
-
self._project_id = project.id
|
|
30
|
-
|
|
31
|
-
############
|
|
32
|
-
# entities #
|
|
33
|
-
############
|
|
34
|
-
@property
|
|
35
|
-
def platform_url(self):
|
|
36
|
-
if self._project_id is None:
|
|
37
|
-
project_id = self.dataset.project.id
|
|
38
|
-
else:
|
|
39
|
-
project_id = self._project_id
|
|
40
|
-
return self._client_api._get_resource_url("projects/{}/recipes".format(project_id))
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def
|
|
44
|
-
if self.
|
|
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
|
-
if
|
|
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
|
-
recipes
|
|
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
|
-
return recipe
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
:
|
|
281
|
-
:
|
|
282
|
-
|
|
283
|
-
**Example**:
|
|
284
|
-
|
|
285
|
-
.. code-block:: python
|
|
286
|
-
|
|
287
|
-
dataset.recipes.
|
|
288
|
-
"""
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
**
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if not
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
:
|
|
334
|
-
:
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
@_api_reference.add(path='/recipes/{id}
|
|
355
|
-
def
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import traceback
|
|
3
|
+
import urllib.parse
|
|
4
|
+
|
|
5
|
+
from .. import entities, miscellaneous, repositories, exceptions, _api_reference
|
|
6
|
+
from ..services.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(name='dtlpy')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Recipes:
|
|
12
|
+
"""
|
|
13
|
+
Recipes Repository
|
|
14
|
+
|
|
15
|
+
The Recipes class allows you to manage recipes and their properties.
|
|
16
|
+
For more information on Recipes, see our `documentation <https://dataloop.ai/docs/ontology>`_ and `developers' documentation <https://developers.dataloop.ai/tutorials/recipe_and_ontology/recipe/chapter/>`_.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self,
|
|
20
|
+
client_api: ApiClient,
|
|
21
|
+
dataset: entities.Dataset = None,
|
|
22
|
+
project: entities.Project = None,
|
|
23
|
+
project_id: str = None):
|
|
24
|
+
self._client_api = client_api
|
|
25
|
+
self._dataset = dataset
|
|
26
|
+
self._project = project
|
|
27
|
+
self._project_id = project_id
|
|
28
|
+
if project_id is None and project is not None:
|
|
29
|
+
self._project_id = project.id
|
|
30
|
+
|
|
31
|
+
############
|
|
32
|
+
# entities #
|
|
33
|
+
############
|
|
34
|
+
@property
|
|
35
|
+
def platform_url(self):
|
|
36
|
+
if self._project_id is None:
|
|
37
|
+
project_id = self.dataset.project.id
|
|
38
|
+
else:
|
|
39
|
+
project_id = self._project_id
|
|
40
|
+
return self._client_api._get_resource_url("projects/{}/recipes".format(project_id))
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def project(self) -> entities.Project:
|
|
44
|
+
if self._project is None:
|
|
45
|
+
if self._project_id is None:
|
|
46
|
+
if self._dataset is None:
|
|
47
|
+
raise exceptions.PlatformException(
|
|
48
|
+
error='2001',
|
|
49
|
+
message='Missing "Project". need to set a Project entity or use project.recipes repository'
|
|
50
|
+
)
|
|
51
|
+
else:
|
|
52
|
+
self._project = self._dataset.project
|
|
53
|
+
self._project_id = self._project.id
|
|
54
|
+
else:
|
|
55
|
+
self._project = repositories.Projects(client_api=self._client_api).get(project_id=self._project_id)
|
|
56
|
+
self._project_id = self._project.id
|
|
57
|
+
|
|
58
|
+
assert isinstance(self._project, entities.Project)
|
|
59
|
+
return self._project
|
|
60
|
+
|
|
61
|
+
@project.setter
|
|
62
|
+
def project(self, project: entities.Project):
|
|
63
|
+
if not isinstance(project, entities.Project):
|
|
64
|
+
raise ValueError('Must input a valid Project entity')
|
|
65
|
+
self._project = project
|
|
66
|
+
self._project_id = project.id
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def dataset(self) -> entities.Dataset:
|
|
70
|
+
if self._dataset is None:
|
|
71
|
+
raise exceptions.PlatformException(
|
|
72
|
+
error='2001',
|
|
73
|
+
message='Missing "dataset". need to set a Dataset entity or use dataset.recipes repository')
|
|
74
|
+
assert isinstance(self._dataset, entities.Dataset)
|
|
75
|
+
return self._dataset
|
|
76
|
+
|
|
77
|
+
@dataset.setter
|
|
78
|
+
def dataset(self, dataset: entities.Dataset):
|
|
79
|
+
if not isinstance(dataset, entities.Dataset):
|
|
80
|
+
raise ValueError('Must input a valid Dataset entity')
|
|
81
|
+
self._dataset = dataset
|
|
82
|
+
|
|
83
|
+
###########
|
|
84
|
+
# methods #
|
|
85
|
+
###########
|
|
86
|
+
@_api_reference.add(path='/recipes', method='post')
|
|
87
|
+
def create(self,
|
|
88
|
+
project_ids=None,
|
|
89
|
+
ontology_ids=None,
|
|
90
|
+
labels=None,
|
|
91
|
+
recipe_name=None,
|
|
92
|
+
attributes=None,
|
|
93
|
+
annotation_instruction_file=None
|
|
94
|
+
) -> entities.Recipe:
|
|
95
|
+
"""
|
|
96
|
+
Create a new Recipe.
|
|
97
|
+
Note: If the param ontology_ids is None, an ontology will be created first.
|
|
98
|
+
|
|
99
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*.
|
|
100
|
+
|
|
101
|
+
:param str project_ids: project ids
|
|
102
|
+
:param str or list ontology_ids: ontology ids
|
|
103
|
+
:param labels: labels
|
|
104
|
+
:param str recipe_name: recipe name
|
|
105
|
+
:param attributes: attributes
|
|
106
|
+
:param str annotation_instruction_file: file path or url of the recipe instruction
|
|
107
|
+
:return: Recipe entity
|
|
108
|
+
:rtype: dtlpy.entities.recipe.Recipe
|
|
109
|
+
|
|
110
|
+
**Example**:
|
|
111
|
+
|
|
112
|
+
.. code-block:: python
|
|
113
|
+
|
|
114
|
+
dataset.recipes.create(recipe_name='My Recipe', labels=labels))
|
|
115
|
+
"""
|
|
116
|
+
if labels is None:
|
|
117
|
+
labels = list()
|
|
118
|
+
if attributes is None:
|
|
119
|
+
attributes = list()
|
|
120
|
+
if project_ids is None:
|
|
121
|
+
if self._dataset is not None:
|
|
122
|
+
project_ids = [self._dataset.project.id]
|
|
123
|
+
else:
|
|
124
|
+
# get from cache
|
|
125
|
+
project = self._client_api.state_io.get('project')
|
|
126
|
+
if project is not None:
|
|
127
|
+
# build entity from json
|
|
128
|
+
p = entities.Project.from_json(_json=project, client_api=self._client_api)
|
|
129
|
+
project_ids = [p.id]
|
|
130
|
+
else:
|
|
131
|
+
# get from self.project property
|
|
132
|
+
try:
|
|
133
|
+
project_ids = [self.project.id]
|
|
134
|
+
except exceptions.PlatformException:
|
|
135
|
+
raise exceptions.PlatformException('Must provide project_ids')
|
|
136
|
+
if ontology_ids is None:
|
|
137
|
+
ontolgies = repositories.Ontologies(client_api=self._client_api,
|
|
138
|
+
recipe=None)
|
|
139
|
+
ontology = ontolgies.create(labels=labels,
|
|
140
|
+
project_ids=project_ids,
|
|
141
|
+
attributes=attributes)
|
|
142
|
+
ontology_ids = [ontology.id]
|
|
143
|
+
elif not isinstance(ontology_ids, list):
|
|
144
|
+
ontology_ids = [ontology_ids]
|
|
145
|
+
if recipe_name is None:
|
|
146
|
+
recipe_name = self._dataset.name + " Default Recipe" if self._dataset is not None else "Default Recipe"
|
|
147
|
+
payload = {'title': recipe_name,
|
|
148
|
+
'projectIds': project_ids,
|
|
149
|
+
'ontologyIds': ontology_ids,
|
|
150
|
+
'uiSettings': {
|
|
151
|
+
"allowObjectIdAutoAssign": True,
|
|
152
|
+
"studioV2App": True
|
|
153
|
+
}}
|
|
154
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
155
|
+
path='/recipes',
|
|
156
|
+
json_req=payload)
|
|
157
|
+
if success:
|
|
158
|
+
recipe = entities.Recipe.from_json(client_api=self._client_api,
|
|
159
|
+
_json=response.json(),
|
|
160
|
+
dataset=self._dataset)
|
|
161
|
+
if annotation_instruction_file:
|
|
162
|
+
recipe.add_instruction(annotation_instruction_file=annotation_instruction_file)
|
|
163
|
+
else:
|
|
164
|
+
logger.error('Failed to create Recipe')
|
|
165
|
+
raise exceptions.PlatformException(response)
|
|
166
|
+
|
|
167
|
+
if self._dataset is not None:
|
|
168
|
+
self._dataset.switch_recipe(recipe.id)
|
|
169
|
+
return recipe
|
|
170
|
+
|
|
171
|
+
def list(self, filters: entities.Filters = None) -> miscellaneous.List[entities.Recipe]:
|
|
172
|
+
"""
|
|
173
|
+
List recipes for a dataset.
|
|
174
|
+
|
|
175
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*.
|
|
176
|
+
|
|
177
|
+
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
178
|
+
:return: list of all recipes
|
|
179
|
+
:retype: list
|
|
180
|
+
|
|
181
|
+
**Example**:
|
|
182
|
+
|
|
183
|
+
.. code-block:: python
|
|
184
|
+
|
|
185
|
+
dataset.recipes.list()
|
|
186
|
+
"""
|
|
187
|
+
if self._dataset is not None:
|
|
188
|
+
try:
|
|
189
|
+
recipes = [recipe_id for recipe_id in self._dataset.metadata['system']['recipes']]
|
|
190
|
+
except KeyError:
|
|
191
|
+
recipes = list()
|
|
192
|
+
|
|
193
|
+
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
194
|
+
jobs = [None for _ in range(len(recipes))]
|
|
195
|
+
for i_recipe, recipe_id in enumerate(recipes):
|
|
196
|
+
jobs[i_recipe] = pool.submit(self._protected_get, **{'recipe_id': recipe_id})
|
|
197
|
+
|
|
198
|
+
# get all results
|
|
199
|
+
results = [j.result() for j in jobs]
|
|
200
|
+
# log errors
|
|
201
|
+
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
202
|
+
# return good jobs
|
|
203
|
+
recipes = miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
204
|
+
elif self._project_id is not None:
|
|
205
|
+
if filters is None:
|
|
206
|
+
filters = entities.Filters(resource=entities.FiltersResource.RECIPE)
|
|
207
|
+
# assert type filters
|
|
208
|
+
elif not isinstance(filters, entities.Filters):
|
|
209
|
+
raise exceptions.PlatformException(error='400',
|
|
210
|
+
message='Unknown filters type: {!r}'.format(type(filters)))
|
|
211
|
+
if filters.resource != entities.FiltersResource.RECIPE:
|
|
212
|
+
raise exceptions.PlatformException(
|
|
213
|
+
error='400',
|
|
214
|
+
message='Filters resource must to be FiltersResource.RECIPE. Got: {!r}'.format(filters.resource))
|
|
215
|
+
if not filters.has_field('projects'):
|
|
216
|
+
filters.add(field='projects', values=[self._project_id])
|
|
217
|
+
|
|
218
|
+
recipes = entities.PagedEntities(items_repository=self,
|
|
219
|
+
filters=filters,
|
|
220
|
+
page_offset=filters.page,
|
|
221
|
+
page_size=filters.page_size,
|
|
222
|
+
project_id=self._project_id,
|
|
223
|
+
client_api=self._client_api)
|
|
224
|
+
recipes.get_page()
|
|
225
|
+
else:
|
|
226
|
+
raise exceptions.PlatformException('400', 'Must have project or dataset entity in repository')
|
|
227
|
+
|
|
228
|
+
return recipes
|
|
229
|
+
|
|
230
|
+
def _list(self, filters: entities.Filters):
|
|
231
|
+
url = filters.generate_url_query_params('/recipes')
|
|
232
|
+
encoded_url = urllib.parse.quote(url, safe='/:?=&')
|
|
233
|
+
# request
|
|
234
|
+
success, response = self._client_api.gen_request(req_type='get', path=encoded_url)
|
|
235
|
+
if not success:
|
|
236
|
+
raise exceptions.PlatformException(response)
|
|
237
|
+
return response.json()
|
|
238
|
+
|
|
239
|
+
def _build_entities_from_response(self, response_items) -> miscellaneous.List[entities.Recipe]:
|
|
240
|
+
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
241
|
+
jobs = [None for _ in range(len(response_items))]
|
|
242
|
+
# return triggers list
|
|
243
|
+
for i_rec, rec in enumerate(response_items):
|
|
244
|
+
jobs[i_rec] = pool.submit(entities.Recipe._protected_from_json,
|
|
245
|
+
**{'client_api': self._client_api,
|
|
246
|
+
'_json': rec,
|
|
247
|
+
'project': self._project,
|
|
248
|
+
'dataset': self._dataset})
|
|
249
|
+
|
|
250
|
+
# get all results
|
|
251
|
+
results = [j.result() for j in jobs]
|
|
252
|
+
# log errors
|
|
253
|
+
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
254
|
+
# return good jobs
|
|
255
|
+
recipes = miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
256
|
+
return recipes
|
|
257
|
+
|
|
258
|
+
def _protected_get(self, recipe_id):
|
|
259
|
+
"""
|
|
260
|
+
Same as get but with try-except to catch if error
|
|
261
|
+
:param recipe_id:
|
|
262
|
+
:return:
|
|
263
|
+
"""
|
|
264
|
+
try:
|
|
265
|
+
recipe = self.get(recipe_id=recipe_id)
|
|
266
|
+
status = True
|
|
267
|
+
except Exception:
|
|
268
|
+
recipe = traceback.format_exc()
|
|
269
|
+
status = False
|
|
270
|
+
return status, recipe
|
|
271
|
+
|
|
272
|
+
@_api_reference.add(path='/recipes/{id}', method='get')
|
|
273
|
+
def get(self, recipe_id: str) -> entities.Recipe:
|
|
274
|
+
"""
|
|
275
|
+
Get a Recipe object to use in your code.
|
|
276
|
+
|
|
277
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*.
|
|
278
|
+
|
|
279
|
+
:param str recipe_id: recipe id
|
|
280
|
+
:return: Recipe object
|
|
281
|
+
:rtype: dtlpy.entities.recipe.Recipe
|
|
282
|
+
|
|
283
|
+
**Example**:
|
|
284
|
+
|
|
285
|
+
.. code-block:: python
|
|
286
|
+
|
|
287
|
+
dataset.recipes.get(recipe_id='recipe_id')
|
|
288
|
+
"""
|
|
289
|
+
success, response = self._client_api.gen_request(req_type='get',
|
|
290
|
+
path='/recipes/%s' % recipe_id)
|
|
291
|
+
if success:
|
|
292
|
+
recipe = entities.Recipe.from_json(client_api=self._client_api,
|
|
293
|
+
_json=response.json(),
|
|
294
|
+
project=self._project,
|
|
295
|
+
dataset=self._dataset)
|
|
296
|
+
else:
|
|
297
|
+
logger.error('Unable to get info from recipe. Recipe_id id: {}'.format(recipe_id))
|
|
298
|
+
raise exceptions.PlatformException(response)
|
|
299
|
+
|
|
300
|
+
return recipe
|
|
301
|
+
|
|
302
|
+
def open_in_web(self,
|
|
303
|
+
recipe: entities.Recipe = None,
|
|
304
|
+
recipe_id: str = None):
|
|
305
|
+
"""
|
|
306
|
+
Open the recipe in web platform.
|
|
307
|
+
|
|
308
|
+
**Prerequisites**: All users.
|
|
309
|
+
|
|
310
|
+
:param dtlpy.entities.recipe.Recipe recipe: recipe entity
|
|
311
|
+
:param str recipe_id: recipe id
|
|
312
|
+
|
|
313
|
+
**Example**:
|
|
314
|
+
|
|
315
|
+
.. code-block:: python
|
|
316
|
+
|
|
317
|
+
dataset.recipes.open_in_web(recipe_id='recipe_id')
|
|
318
|
+
"""
|
|
319
|
+
if recipe is not None:
|
|
320
|
+
recipe.open_in_web()
|
|
321
|
+
elif recipe_id is not None:
|
|
322
|
+
self._client_api._open_in_web(url=self.platform_url + '/' + str(recipe_id))
|
|
323
|
+
else:
|
|
324
|
+
self._client_api._open_in_web(url=self.platform_url)
|
|
325
|
+
|
|
326
|
+
@_api_reference.add(path='/recipes/{id}', method='delete')
|
|
327
|
+
def delete(self, recipe_id: str, force: bool = False):
|
|
328
|
+
"""
|
|
329
|
+
Delete recipe from platform.
|
|
330
|
+
|
|
331
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*.
|
|
332
|
+
|
|
333
|
+
:param str recipe_id: recipe id
|
|
334
|
+
:param bool force: force delete recipe
|
|
335
|
+
:return: True if success
|
|
336
|
+
:rtype: bool
|
|
337
|
+
|
|
338
|
+
**Example**:
|
|
339
|
+
|
|
340
|
+
.. code-block:: python
|
|
341
|
+
|
|
342
|
+
dataset.recipes.delete(recipe_id='recipe_id')
|
|
343
|
+
"""
|
|
344
|
+
path = '/recipes/{}'.format(recipe_id)
|
|
345
|
+
if force:
|
|
346
|
+
path += '?force=true'
|
|
347
|
+
success, response = self._client_api.gen_request(req_type='delete',
|
|
348
|
+
path=path)
|
|
349
|
+
if not success:
|
|
350
|
+
raise exceptions.PlatformException(response)
|
|
351
|
+
logger.info('Recipe id {} deleted successfully'.format(recipe_id))
|
|
352
|
+
return True
|
|
353
|
+
|
|
354
|
+
@_api_reference.add(path='/recipes/{id}', method='patch')
|
|
355
|
+
def update(self, recipe: entities.Recipe, system_metadata=False) -> entities.Recipe:
|
|
356
|
+
"""
|
|
357
|
+
Update recipe.
|
|
358
|
+
|
|
359
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*.
|
|
360
|
+
|
|
361
|
+
:param dtlpy.entities.recipe.Recipe recipe: Recipe object
|
|
362
|
+
:param bool system_metadata: True, if you want to change metadata system
|
|
363
|
+
:return: Recipe object
|
|
364
|
+
:rtype: dtlpy.entities.recipe.Recipe
|
|
365
|
+
|
|
366
|
+
**Example**:
|
|
367
|
+
|
|
368
|
+
.. code-block:: python
|
|
369
|
+
|
|
370
|
+
dataset.recipes.update(recipe='recipe_entity')
|
|
371
|
+
"""
|
|
372
|
+
url_path = '/recipes/%s' % recipe.id
|
|
373
|
+
if system_metadata:
|
|
374
|
+
url_path += '?system=true'
|
|
375
|
+
success, response = self._client_api.gen_request(req_type='patch',
|
|
376
|
+
path=url_path,
|
|
377
|
+
json_req=recipe.to_json())
|
|
378
|
+
if success:
|
|
379
|
+
return entities.Recipe.from_json(client_api=self._client_api, _json=response.json(), dataset=self._dataset)
|
|
380
|
+
else:
|
|
381
|
+
logger.error('Error while updating item:')
|
|
382
|
+
raise exceptions.PlatformException(response)
|
|
383
|
+
|
|
384
|
+
@_api_reference.add(path='/recipes/{id}/clone', method='post')
|
|
385
|
+
def clone(self,
|
|
386
|
+
recipe: entities.Recipe = None,
|
|
387
|
+
recipe_id: str = None,
|
|
388
|
+
shallow: bool = False):
|
|
389
|
+
"""
|
|
390
|
+
Clone recipe.
|
|
391
|
+
|
|
392
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*.
|
|
393
|
+
|
|
394
|
+
:param dtlpy.entities.recipe.Recipe recipe: Recipe object
|
|
395
|
+
:param str recipe_id: Recipe id
|
|
396
|
+
:param bool shallow: If True, link to existing ontology, clones all ontologies that are linked to the recipe as well
|
|
397
|
+
:return: Cloned ontology object
|
|
398
|
+
:rtype: dtlpy.entities.recipe.Recipe
|
|
399
|
+
|
|
400
|
+
**Example**:
|
|
401
|
+
|
|
402
|
+
.. code-block:: python
|
|
403
|
+
|
|
404
|
+
dataset.recipes.clone(recipe_id='recipe_id')
|
|
405
|
+
"""
|
|
406
|
+
if recipe is None and recipe_id is None:
|
|
407
|
+
raise exceptions.PlatformException('400', 'Must provide recipe or recipe_id')
|
|
408
|
+
if recipe_id is None:
|
|
409
|
+
if not isinstance(recipe, entities.Recipe):
|
|
410
|
+
raise exceptions.PlatformException('400', 'Recipe must me entities.Recipe type')
|
|
411
|
+
else:
|
|
412
|
+
recipe_id = recipe.id
|
|
413
|
+
|
|
414
|
+
payload = {'shallow': shallow}
|
|
415
|
+
|
|
416
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
417
|
+
path='/recipes/{}/clone'.format(recipe_id),
|
|
418
|
+
json_req=payload)
|
|
419
|
+
if success:
|
|
420
|
+
recipe = entities.Recipe.from_json(client_api=self._client_api,
|
|
421
|
+
_json=response.json())
|
|
422
|
+
else:
|
|
423
|
+
logger.error('Failed to clone Recipe')
|
|
424
|
+
raise exceptions.PlatformException(response)
|
|
425
|
+
|
|
426
|
+
assert isinstance(recipe, entities.Recipe)
|
|
427
|
+
logger.debug('Recipe has been cloned successfully. recipe id: {}'.format(recipe.id))
|
|
428
|
+
|
|
429
|
+
return recipe
|