dtlpy 1.114.17__py3-none-any.whl → 1.116.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 -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 +292 -296
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -442
- dtlpy/entities/dataset.py +1299 -1285
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -223
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +798 -645
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +959 -953
- 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 -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 +963 -958
- 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 +1257 -1086
- 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 -158
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -435
- dtlpy/repositories/datasets.py +1504 -1291
- dtlpy/repositories/downloader.py +976 -903
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -470
- dtlpy/repositories/executions.py +815 -817
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +255 -238
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -909
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -988
- 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 +419 -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 -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 +1785 -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 +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.114.17.data → dtlpy-1.116.6.data}/scripts/dlp +1 -1
- dtlpy-1.116.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.114.17.data → dtlpy-1.116.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/METADATA +186 -183
- dtlpy-1.116.6.dist-info/RECORD +239 -0
- {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.114.17.data/scripts/dlp.bat +0 -2
- dtlpy-1.114.17.dist-info/RECORD +0 -240
- {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/top_level.txt +0 -0
dtlpy/repositories/projects.py
CHANGED
|
@@ -1,539 +1,539 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from urllib.parse import quote
|
|
3
|
-
import jwt
|
|
4
|
-
|
|
5
|
-
from .. import entities, miscellaneous, exceptions, _api_reference
|
|
6
|
-
from ..services.api_client import ApiClient
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger(name='dtlpy')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Projects:
|
|
12
|
-
"""
|
|
13
|
-
Projects Repository
|
|
14
|
-
|
|
15
|
-
The Projects class allows the user to manage projects and their properties.
|
|
16
|
-
|
|
17
|
-
For more information on Projects see the `Dataloop documentation <https://dataloop.ai/docs/project#>`_.
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
def __init__(self, client_api: ApiClient, org=None):
|
|
21
|
-
self._client_api = client_api
|
|
22
|
-
self._org = org
|
|
23
|
-
|
|
24
|
-
def __get_from_cache(self) -> entities.Project:
|
|
25
|
-
project = self._client_api.state_io.get('project')
|
|
26
|
-
if project is not None:
|
|
27
|
-
project = entities.Project.from_json(_json=project, client_api=self._client_api)
|
|
28
|
-
return project
|
|
29
|
-
|
|
30
|
-
def __get_by_id(self, project_id: str, log_error: bool) -> entities.Project:
|
|
31
|
-
"""
|
|
32
|
-
:param project_id:
|
|
33
|
-
"""
|
|
34
|
-
success, response = self._client_api.gen_request(
|
|
35
|
-
req_type='get',
|
|
36
|
-
path='/projects/{}'.format(project_id),
|
|
37
|
-
log_error=log_error
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
try:
|
|
41
|
-
response_json = response.json()
|
|
42
|
-
except Exception:
|
|
43
|
-
try:
|
|
44
|
-
logger.exception('Failed to parse response content: {}'.format(response.text))
|
|
45
|
-
except Exception:
|
|
46
|
-
logger.exception('Failed to print response content')
|
|
47
|
-
raise
|
|
48
|
-
|
|
49
|
-
if success:
|
|
50
|
-
project = entities.Project.from_json(
|
|
51
|
-
client_api=self._client_api,
|
|
52
|
-
_json=response_json
|
|
53
|
-
)
|
|
54
|
-
else:
|
|
55
|
-
# raise PlatformException(response)
|
|
56
|
-
# TODO because of a bug in gate wrong error is returned so for now manually raise not found
|
|
57
|
-
raise exceptions.PlatformException(error="404", message="Project not found")
|
|
58
|
-
return project
|
|
59
|
-
|
|
60
|
-
def __get_by_name(self, project_name: str):
|
|
61
|
-
"""
|
|
62
|
-
:param project_name:
|
|
63
|
-
"""
|
|
64
|
-
project_name = quote(project_name.encode("utf-8"))
|
|
65
|
-
success, response = self._client_api.gen_request(req_type='get',
|
|
66
|
-
path='/projects/{}/name'.format(project_name))
|
|
67
|
-
if success:
|
|
68
|
-
projects = [entities.Project.from_json(client_api=self._client_api,
|
|
69
|
-
_json=project_json) for project_json in response.json()]
|
|
70
|
-
else:
|
|
71
|
-
# TODO because of a bug in gate wrong error is returned so for now manually raise not found
|
|
72
|
-
raise exceptions.PlatformException(error="404", message="Project not found")
|
|
73
|
-
return projects
|
|
74
|
-
|
|
75
|
-
def __get_by_identifier(self, identifier=None) -> entities.Project:
|
|
76
|
-
"""
|
|
77
|
-
:param identifier:
|
|
78
|
-
"""
|
|
79
|
-
projects = self.list()
|
|
80
|
-
projects_by_name = [project for project in projects if identifier in project.id or identifier in project.name]
|
|
81
|
-
if len(projects_by_name) == 1:
|
|
82
|
-
return projects_by_name[0]
|
|
83
|
-
elif len(projects_by_name) > 1:
|
|
84
|
-
raise Exception('Multiple projects with this name/identifier exist')
|
|
85
|
-
else:
|
|
86
|
-
raise Exception("Project not found")
|
|
87
|
-
|
|
88
|
-
@property
|
|
89
|
-
def platform_url(self):
|
|
90
|
-
return self._client_api._get_resource_url("projects")
|
|
91
|
-
|
|
92
|
-
def open_in_web(self,
|
|
93
|
-
project_name: str = None,
|
|
94
|
-
project_id: str = None,
|
|
95
|
-
project: entities.Project = None):
|
|
96
|
-
"""
|
|
97
|
-
Open the project in our web platform.
|
|
98
|
-
|
|
99
|
-
**Prerequisites**: All users can open a project in the web.
|
|
100
|
-
|
|
101
|
-
:param str project_name: The Name of the project
|
|
102
|
-
:param str project_id: The Id of the project
|
|
103
|
-
:param dtlpy.entities.project.Project project: project object
|
|
104
|
-
|
|
105
|
-
**Example**:
|
|
106
|
-
|
|
107
|
-
.. code-block:: python
|
|
108
|
-
|
|
109
|
-
dl.projects.open_in_web(project_id='project_id')
|
|
110
|
-
"""
|
|
111
|
-
if project_name is not None:
|
|
112
|
-
project = self.get(project_name=project_name)
|
|
113
|
-
if project is not None:
|
|
114
|
-
project.open_in_web()
|
|
115
|
-
elif project_id is not None:
|
|
116
|
-
self._client_api._open_in_web(url=self.platform_url + '/' + str(project_id))
|
|
117
|
-
else:
|
|
118
|
-
self._client_api._open_in_web(url=self.platform_url)
|
|
119
|
-
|
|
120
|
-
def checkout(self,
|
|
121
|
-
identifier: str = None,
|
|
122
|
-
project_name: str = None,
|
|
123
|
-
project_id: str = None,
|
|
124
|
-
project: entities.Project = None):
|
|
125
|
-
"""
|
|
126
|
-
Checkout (switch) to a project to work on.
|
|
127
|
-
|
|
128
|
-
**Prerequisites**: All users can open a project in the web.
|
|
129
|
-
|
|
130
|
-
You must provide at least ONE of the following params: project_id, project_name.
|
|
131
|
-
|
|
132
|
-
:param str identifier: project name or partial id that you wish to switch
|
|
133
|
-
:param str project_name: The Name of the project
|
|
134
|
-
:param str project_id: The Id of the project
|
|
135
|
-
:param dtlpy.entities.project.Project project: project object
|
|
136
|
-
|
|
137
|
-
**Example**:
|
|
138
|
-
|
|
139
|
-
.. code-block:: python
|
|
140
|
-
|
|
141
|
-
dl.projects.checkout(project_id='project_id')
|
|
142
|
-
"""
|
|
143
|
-
if project is None:
|
|
144
|
-
if project_id is not None or project_name is not None:
|
|
145
|
-
project = self.get(project_id=project_id, project_name=project_name)
|
|
146
|
-
elif identifier is not None:
|
|
147
|
-
project = self.__get_by_identifier(identifier=identifier)
|
|
148
|
-
else:
|
|
149
|
-
raise exceptions.PlatformException(error='400',
|
|
150
|
-
message='Must provide partial/full id/name to checkout')
|
|
151
|
-
self._client_api.state_io.put('project', project.to_json())
|
|
152
|
-
logger.info('Checked out to project {}'.format(project.name))
|
|
153
|
-
|
|
154
|
-
def _send_mail(self, project_id: str, send_to: str, title: str, content: str) -> bool:
|
|
155
|
-
if project_id:
|
|
156
|
-
url = '/projects/{}/mail'.format(project_id)
|
|
157
|
-
else:
|
|
158
|
-
url = '/outbox'
|
|
159
|
-
assert isinstance(title, str)
|
|
160
|
-
assert isinstance(content, str)
|
|
161
|
-
if self._client_api.token is not None:
|
|
162
|
-
sender = jwt.decode(self._client_api.token, algorithms=['HS256'],
|
|
163
|
-
verify=False, options={'verify_signature': False})['email']
|
|
164
|
-
else:
|
|
165
|
-
raise exceptions.PlatformException('600', 'Token expired please log in')
|
|
166
|
-
|
|
167
|
-
payload = {
|
|
168
|
-
'to': send_to,
|
|
169
|
-
'from': sender,
|
|
170
|
-
'subject': title,
|
|
171
|
-
'body': content
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
175
|
-
path=url,
|
|
176
|
-
json_req=payload)
|
|
177
|
-
|
|
178
|
-
if not success:
|
|
179
|
-
raise exceptions.PlatformException(response)
|
|
180
|
-
return True
|
|
181
|
-
|
|
182
|
-
@_api_reference.add(path='/projects/{projectId}/members/{userId}', method='post')
|
|
183
|
-
def add_member(self, email: str, project_id: str, role: entities.MemberRole = entities.MemberRole.DEVELOPER):
|
|
184
|
-
"""
|
|
185
|
-
Add a member to the project.
|
|
186
|
-
|
|
187
|
-
**Prerequisites**: You must be in the role of an *owner* to add a member to a project.
|
|
188
|
-
|
|
189
|
-
:param str email: member email
|
|
190
|
-
:param str project_id: The Id of the project
|
|
191
|
-
:param role: The required role for the user. Use the enum dl.MemberRole
|
|
192
|
-
:return: dict that represent the user
|
|
193
|
-
:rtype: dict
|
|
194
|
-
|
|
195
|
-
**Example**:
|
|
196
|
-
|
|
197
|
-
.. code-block:: python
|
|
198
|
-
|
|
199
|
-
user_json = dl.projects.add_member(project_id='project_id', email='user@dataloop.ai', role=dl.MemberRole.DEVELOPER)
|
|
200
|
-
"""
|
|
201
|
-
url_path = '/projects/{}/members/{}'.format(project_id, email)
|
|
202
|
-
payload = dict(role=role)
|
|
203
|
-
|
|
204
|
-
if role not in list(entities.MemberRole):
|
|
205
|
-
raise ValueError('Unknown role {!r}, role must be one of: {}'.format(role,
|
|
206
|
-
', '.join(list(entities.MemberRole))))
|
|
207
|
-
|
|
208
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
209
|
-
path=url_path,
|
|
210
|
-
json_req=payload)
|
|
211
|
-
if not success:
|
|
212
|
-
raise exceptions.PlatformException(response)
|
|
213
|
-
|
|
214
|
-
return response.json()
|
|
215
|
-
|
|
216
|
-
@_api_reference.add(path='/projects/{projectId}/members/{userId}', method='patch')
|
|
217
|
-
def update_member(self, email: str, project_id: str, role: entities.MemberRole = entities.MemberRole.DEVELOPER):
|
|
218
|
-
"""
|
|
219
|
-
Update member's information/details in the project.
|
|
220
|
-
|
|
221
|
-
**Prerequisites**: You must be in the role of an *owner* to update a member.
|
|
222
|
-
|
|
223
|
-
:param str email: member email
|
|
224
|
-
:param str project_id: The Id of the project
|
|
225
|
-
:param role: The required role for the user. Use the enum dl.MemberRole
|
|
226
|
-
:return: dict that represent the user
|
|
227
|
-
:rtype: dict
|
|
228
|
-
|
|
229
|
-
**Example**:
|
|
230
|
-
|
|
231
|
-
.. code-block:: python
|
|
232
|
-
|
|
233
|
-
user_json = = dl.projects.update_member(project_id='project_id', email='user@dataloop.ai', role=dl.MemberRole.DEVELOPER)
|
|
234
|
-
"""
|
|
235
|
-
url_path = '/projects/{}/members/{}'.format(project_id, email)
|
|
236
|
-
payload = dict(role=role)
|
|
237
|
-
|
|
238
|
-
if role not in list(entities.MemberRole):
|
|
239
|
-
raise ValueError('Unknown role {!r}, role must be one of: {}'.format(role,
|
|
240
|
-
', '.join(list(entities.MemberRole))))
|
|
241
|
-
|
|
242
|
-
success, response = self._client_api.gen_request(req_type='patch',
|
|
243
|
-
path=url_path,
|
|
244
|
-
json_req=payload)
|
|
245
|
-
if not success:
|
|
246
|
-
raise exceptions.PlatformException(response)
|
|
247
|
-
|
|
248
|
-
return response.json()
|
|
249
|
-
|
|
250
|
-
@_api_reference.add(path='/projects/{projectId}/members/{userId}', method='delete')
|
|
251
|
-
def remove_member(self, email: str, project_id: str):
|
|
252
|
-
"""
|
|
253
|
-
Remove a member from the project.
|
|
254
|
-
|
|
255
|
-
**Prerequisites**: You must be in the role of an *owner* to delete a member from a project.
|
|
256
|
-
|
|
257
|
-
:param str email: member email
|
|
258
|
-
:param str project_id: The Id of the project
|
|
259
|
-
:return: dict that represents the user
|
|
260
|
-
:rtype: dict
|
|
261
|
-
|
|
262
|
-
**Example**:
|
|
263
|
-
|
|
264
|
-
.. code-block:: python
|
|
265
|
-
|
|
266
|
-
user_json = dl.projects.remove_member(project_id='project_id', email='user@dataloop.ai')
|
|
267
|
-
"""
|
|
268
|
-
url_path = '/projects/{}/members/{}'.format(project_id, email)
|
|
269
|
-
success, response = self._client_api.gen_request(req_type='delete',
|
|
270
|
-
path=url_path)
|
|
271
|
-
if not success:
|
|
272
|
-
raise exceptions.PlatformException(response)
|
|
273
|
-
|
|
274
|
-
return response.json()
|
|
275
|
-
|
|
276
|
-
@_api_reference.add(path='/projects/{projectId}/members', method='get')
|
|
277
|
-
def list_members(self, project: entities.Project, role: entities.MemberRole = None):
|
|
278
|
-
"""
|
|
279
|
-
Get a list of the project members.
|
|
280
|
-
|
|
281
|
-
**Prerequisites**: You must be in the role of an *owner* to list project members.
|
|
282
|
-
|
|
283
|
-
:param dtlpy.entities.project.Project project: Project object
|
|
284
|
-
:param role: The required role for the user. Use the enum dl.MemberRole
|
|
285
|
-
:return: list of the project members
|
|
286
|
-
:rtype: list
|
|
287
|
-
|
|
288
|
-
**Example**:
|
|
289
|
-
|
|
290
|
-
.. code-block:: python
|
|
291
|
-
|
|
292
|
-
users_jsons_list = dl.projects.list_members(project_id='project_id', role=dl.MemberRole.DEVELOPER)
|
|
293
|
-
"""
|
|
294
|
-
url_path = '/projects/{}/members'.format(project.id)
|
|
295
|
-
|
|
296
|
-
if role is not None and role not in list(entities.MemberRole):
|
|
297
|
-
raise ValueError('Unknown role {!r}, role must be one of: {}'.format(role,
|
|
298
|
-
', '.join(list(entities.MemberRole))))
|
|
299
|
-
|
|
300
|
-
success, response = self._client_api.gen_request(req_type='get',
|
|
301
|
-
path=url_path)
|
|
302
|
-
if not success:
|
|
303
|
-
raise exceptions.PlatformException(response)
|
|
304
|
-
|
|
305
|
-
members = miscellaneous.List(
|
|
306
|
-
[entities.User.from_json(_json=user, client_api=self._client_api, project=project) for user in
|
|
307
|
-
response.json()])
|
|
308
|
-
|
|
309
|
-
if role is not None:
|
|
310
|
-
members = [member for member in members if member.role == role]
|
|
311
|
-
|
|
312
|
-
return members
|
|
313
|
-
|
|
314
|
-
@_api_reference.add(path='/projects', method='get')
|
|
315
|
-
def list(self) -> miscellaneous.List[entities.Project]:
|
|
316
|
-
"""
|
|
317
|
-
Get the user's project list
|
|
318
|
-
|
|
319
|
-
**Prerequisites**: You must be a **superuser** to list all users' projects.
|
|
320
|
-
|
|
321
|
-
:return: List of Project objects
|
|
322
|
-
|
|
323
|
-
**Example**:
|
|
324
|
-
|
|
325
|
-
.. code-block:: python
|
|
326
|
-
|
|
327
|
-
projects = dl.projects.list()
|
|
328
|
-
"""
|
|
329
|
-
if self._org is None:
|
|
330
|
-
url_path = '/projects'
|
|
331
|
-
else:
|
|
332
|
-
url_path = '/orgs/{}/projects'.format(self._org.id)
|
|
333
|
-
success, response = self._client_api.gen_request(req_type='get',
|
|
334
|
-
path=url_path)
|
|
335
|
-
|
|
336
|
-
if success:
|
|
337
|
-
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
338
|
-
projects_json = response.json()
|
|
339
|
-
jobs = [None for _ in range(len(projects_json))]
|
|
340
|
-
# return triggers list
|
|
341
|
-
for i_project, project in enumerate(projects_json):
|
|
342
|
-
jobs[i_project] = pool.submit(entities.Project._protected_from_json,
|
|
343
|
-
**{'client_api': self._client_api,
|
|
344
|
-
'_json': project})
|
|
345
|
-
|
|
346
|
-
# get all results
|
|
347
|
-
results = [j.result() for j in jobs]
|
|
348
|
-
# log errors
|
|
349
|
-
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
350
|
-
# return good jobs
|
|
351
|
-
projects = miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
352
|
-
else:
|
|
353
|
-
logger.error('Platform error getting projects')
|
|
354
|
-
raise exceptions.PlatformException(response)
|
|
355
|
-
return projects
|
|
356
|
-
|
|
357
|
-
@_api_reference.add(path='/projects/{projectId}', method='get')
|
|
358
|
-
def get(self,
|
|
359
|
-
project_name: str = None,
|
|
360
|
-
project_id: str = None,
|
|
361
|
-
checkout: bool = False,
|
|
362
|
-
fetch: bool = None,
|
|
363
|
-
log_error=True) -> entities.Project:
|
|
364
|
-
"""
|
|
365
|
-
Get a Project object.
|
|
366
|
-
|
|
367
|
-
**Prerequisites**: You must be in the role of an *owner* to get a project object.
|
|
368
|
-
|
|
369
|
-
You must check out to a project or provide at least one of the following params: project_id, project_name
|
|
370
|
-
|
|
371
|
-
:param str project_name: optional - search by name
|
|
372
|
-
:param str project_id: optional - search by id
|
|
373
|
-
:param bool checkout: set the project as a default project object (cookies)
|
|
374
|
-
:param bool fetch: optional - fetch entity from platform (True), default taken from cookie
|
|
375
|
-
:param bool log_error: optional - show the logs errors
|
|
376
|
-
:return: Project object
|
|
377
|
-
:rtype: dtlpy.entities.project.Project
|
|
378
|
-
|
|
379
|
-
**Example**:
|
|
380
|
-
|
|
381
|
-
.. code-block:: python
|
|
382
|
-
|
|
383
|
-
project = dl.projects.get(project_id='project_id')
|
|
384
|
-
"""
|
|
385
|
-
if fetch is None:
|
|
386
|
-
fetch = self._client_api.fetch_entities
|
|
387
|
-
|
|
388
|
-
if project_id is None and project_name is None:
|
|
389
|
-
project = self.__get_from_cache()
|
|
390
|
-
if project is None:
|
|
391
|
-
raise exceptions.PlatformException(
|
|
392
|
-
error='400',
|
|
393
|
-
message='No checked-out Project was found. You must checkout to a project or provide an identifier in inputs')
|
|
394
|
-
elif fetch:
|
|
395
|
-
if project_id is not None:
|
|
396
|
-
if not isinstance(project_id, str):
|
|
397
|
-
raise exceptions.PlatformException(
|
|
398
|
-
error='400',
|
|
399
|
-
message='project_id must be strings')
|
|
400
|
-
|
|
401
|
-
project = self.__get_by_id(project_id, log_error=log_error)
|
|
402
|
-
# verify input project name is same as the given id
|
|
403
|
-
if project_name is not None and project.name != project_name:
|
|
404
|
-
logger.warning(
|
|
405
|
-
"Mismatch found in projects.get: project_name is different then project.name:"
|
|
406
|
-
" {!r} != {!r}".format(
|
|
407
|
-
project_name,
|
|
408
|
-
project.name))
|
|
409
|
-
elif project_name is not None:
|
|
410
|
-
if not isinstance(project_name, str):
|
|
411
|
-
raise exceptions.PlatformException(
|
|
412
|
-
error='400',
|
|
413
|
-
message='project_name must be strings')
|
|
414
|
-
|
|
415
|
-
projects = self.__get_by_name(project_name)
|
|
416
|
-
if len(projects) > 1:
|
|
417
|
-
# more than one matching project
|
|
418
|
-
raise exceptions.PlatformException(
|
|
419
|
-
error='404',
|
|
420
|
-
message='More than one project with same name. Please "get" by id')
|
|
421
|
-
else:
|
|
422
|
-
project = projects[0]
|
|
423
|
-
else:
|
|
424
|
-
raise exceptions.PlatformException(
|
|
425
|
-
error='404',
|
|
426
|
-
message='No input and no checked-out found')
|
|
427
|
-
else:
|
|
428
|
-
project = entities.Project.from_json(_json={'id': project_id,
|
|
429
|
-
'name': project_name},
|
|
430
|
-
client_api=self._client_api,
|
|
431
|
-
is_fetched=False)
|
|
432
|
-
assert isinstance(project, entities.Project)
|
|
433
|
-
if checkout:
|
|
434
|
-
self.checkout(project=project)
|
|
435
|
-
return project
|
|
436
|
-
|
|
437
|
-
@_api_reference.add(path='/projects/{projectId}', method='delete')
|
|
438
|
-
def delete(self,
|
|
439
|
-
project_name: str = None,
|
|
440
|
-
project_id: str = None,
|
|
441
|
-
sure: bool = False,
|
|
442
|
-
really: bool = False) -> bool:
|
|
443
|
-
"""
|
|
444
|
-
Delete a project forever!
|
|
445
|
-
|
|
446
|
-
**Prerequisites**: You must be in the role of an *owner* to delete a project.
|
|
447
|
-
|
|
448
|
-
:param str project_name: optional - search by name
|
|
449
|
-
:param str project_id: optional - search by id
|
|
450
|
-
:param bool sure: Are you sure you want to delete?
|
|
451
|
-
:param bool really: Really really sure?
|
|
452
|
-
:return: True if success, error if not
|
|
453
|
-
:rtype: bool
|
|
454
|
-
|
|
455
|
-
**Example**:
|
|
456
|
-
|
|
457
|
-
.. code-block:: python
|
|
458
|
-
|
|
459
|
-
is_deleted = dl.projects.delete(project_id='project_id', sure=True, really=True)
|
|
460
|
-
"""
|
|
461
|
-
if sure and really:
|
|
462
|
-
if project_id is None:
|
|
463
|
-
project = self.get(project_name=project_name)
|
|
464
|
-
project_id = project.id
|
|
465
|
-
success, response = self._client_api.gen_request(req_type='delete',
|
|
466
|
-
path='/projects/{}'.format(project_id))
|
|
467
|
-
if not success:
|
|
468
|
-
raise exceptions.PlatformException(response)
|
|
469
|
-
logger.info('Project id {} deleted successfully'.format(project_id))
|
|
470
|
-
return True
|
|
471
|
-
else:
|
|
472
|
-
raise exceptions.PlatformException(
|
|
473
|
-
error='403',
|
|
474
|
-
message='Cant delete project from SDK. Please login to platform to delete')
|
|
475
|
-
|
|
476
|
-
@_api_reference.add(path='/projects/{projectId}', method='patch')
|
|
477
|
-
def update(self,
|
|
478
|
-
project: entities.Project,
|
|
479
|
-
system_metadata: bool = False) -> entities.Project:
|
|
480
|
-
"""
|
|
481
|
-
Update a project information (e.g., name, member roles, etc.).
|
|
482
|
-
|
|
483
|
-
**Prerequisites**: You must be in the role of an *owner* to add a member to a project.
|
|
484
|
-
|
|
485
|
-
:param dtlpy.entities.project.Project project: project object
|
|
486
|
-
:param bool system_metadata: optional - True, if you want to change metadata system
|
|
487
|
-
:return: Project object
|
|
488
|
-
:rtype: dtlpy.entities.project.Project
|
|
489
|
-
|
|
490
|
-
**Example**:
|
|
491
|
-
|
|
492
|
-
.. code-block:: python
|
|
493
|
-
|
|
494
|
-
project = dl.projects.delete(project='project_entity')
|
|
495
|
-
"""
|
|
496
|
-
url_path = '/projects/{}'.format(project.id)
|
|
497
|
-
if system_metadata:
|
|
498
|
-
url_path += '?system=true'
|
|
499
|
-
success, response = self._client_api.gen_request(req_type='patch',
|
|
500
|
-
path=url_path,
|
|
501
|
-
json_req=project.to_json())
|
|
502
|
-
if success:
|
|
503
|
-
return project
|
|
504
|
-
else:
|
|
505
|
-
raise exceptions.PlatformException(response)
|
|
506
|
-
|
|
507
|
-
@_api_reference.add(path='/projects', method='post')
|
|
508
|
-
def create(self,
|
|
509
|
-
project_name: str,
|
|
510
|
-
checkout: bool = False) -> entities.Project:
|
|
511
|
-
"""
|
|
512
|
-
Create a new project.
|
|
513
|
-
|
|
514
|
-
**Prerequisites**: Any user can create a project.
|
|
515
|
-
|
|
516
|
-
:param str project_name: The Name of the project
|
|
517
|
-
:param bool checkout: set the project as a default project object (cookies)
|
|
518
|
-
:return: Project object
|
|
519
|
-
:rtype: dtlpy.entities.project.Project
|
|
520
|
-
|
|
521
|
-
**Example**:
|
|
522
|
-
|
|
523
|
-
.. code-block:: python
|
|
524
|
-
|
|
525
|
-
project = dl.projects.create(project_name='project_name')
|
|
526
|
-
"""
|
|
527
|
-
payload = {'name': project_name}
|
|
528
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
529
|
-
path='/projects',
|
|
530
|
-
data=payload)
|
|
531
|
-
if success:
|
|
532
|
-
project = entities.Project.from_json(client_api=self._client_api,
|
|
533
|
-
_json=response.json())
|
|
534
|
-
else:
|
|
535
|
-
raise exceptions.PlatformException(response)
|
|
536
|
-
assert isinstance(project, entities.Project)
|
|
537
|
-
if checkout:
|
|
538
|
-
self.checkout(project=project)
|
|
539
|
-
return project
|
|
1
|
+
import logging
|
|
2
|
+
from urllib.parse import quote
|
|
3
|
+
import jwt
|
|
4
|
+
|
|
5
|
+
from .. import entities, miscellaneous, exceptions, _api_reference
|
|
6
|
+
from ..services.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(name='dtlpy')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Projects:
|
|
12
|
+
"""
|
|
13
|
+
Projects Repository
|
|
14
|
+
|
|
15
|
+
The Projects class allows the user to manage projects and their properties.
|
|
16
|
+
|
|
17
|
+
For more information on Projects see the `Dataloop documentation <https://dataloop.ai/docs/project#>`_.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, client_api: ApiClient, org=None):
|
|
21
|
+
self._client_api = client_api
|
|
22
|
+
self._org = org
|
|
23
|
+
|
|
24
|
+
def __get_from_cache(self) -> entities.Project:
|
|
25
|
+
project = self._client_api.state_io.get('project')
|
|
26
|
+
if project is not None:
|
|
27
|
+
project = entities.Project.from_json(_json=project, client_api=self._client_api)
|
|
28
|
+
return project
|
|
29
|
+
|
|
30
|
+
def __get_by_id(self, project_id: str, log_error: bool) -> entities.Project:
|
|
31
|
+
"""
|
|
32
|
+
:param project_id:
|
|
33
|
+
"""
|
|
34
|
+
success, response = self._client_api.gen_request(
|
|
35
|
+
req_type='get',
|
|
36
|
+
path='/projects/{}'.format(project_id),
|
|
37
|
+
log_error=log_error
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
response_json = response.json()
|
|
42
|
+
except Exception:
|
|
43
|
+
try:
|
|
44
|
+
logger.exception('Failed to parse response content: {}'.format(response.text))
|
|
45
|
+
except Exception:
|
|
46
|
+
logger.exception('Failed to print response content')
|
|
47
|
+
raise
|
|
48
|
+
|
|
49
|
+
if success:
|
|
50
|
+
project = entities.Project.from_json(
|
|
51
|
+
client_api=self._client_api,
|
|
52
|
+
_json=response_json
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
# raise PlatformException(response)
|
|
56
|
+
# TODO because of a bug in gate wrong error is returned so for now manually raise not found
|
|
57
|
+
raise exceptions.PlatformException(error="404", message="Project not found")
|
|
58
|
+
return project
|
|
59
|
+
|
|
60
|
+
def __get_by_name(self, project_name: str):
|
|
61
|
+
"""
|
|
62
|
+
:param project_name:
|
|
63
|
+
"""
|
|
64
|
+
project_name = quote(project_name.encode("utf-8"))
|
|
65
|
+
success, response = self._client_api.gen_request(req_type='get',
|
|
66
|
+
path='/projects/{}/name'.format(project_name))
|
|
67
|
+
if success:
|
|
68
|
+
projects = [entities.Project.from_json(client_api=self._client_api,
|
|
69
|
+
_json=project_json) for project_json in response.json()]
|
|
70
|
+
else:
|
|
71
|
+
# TODO because of a bug in gate wrong error is returned so for now manually raise not found
|
|
72
|
+
raise exceptions.PlatformException(error="404", message="Project not found")
|
|
73
|
+
return projects
|
|
74
|
+
|
|
75
|
+
def __get_by_identifier(self, identifier=None) -> entities.Project:
|
|
76
|
+
"""
|
|
77
|
+
:param identifier:
|
|
78
|
+
"""
|
|
79
|
+
projects = self.list()
|
|
80
|
+
projects_by_name = [project for project in projects if identifier in project.id or identifier in project.name]
|
|
81
|
+
if len(projects_by_name) == 1:
|
|
82
|
+
return projects_by_name[0]
|
|
83
|
+
elif len(projects_by_name) > 1:
|
|
84
|
+
raise Exception('Multiple projects with this name/identifier exist')
|
|
85
|
+
else:
|
|
86
|
+
raise Exception("Project not found")
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def platform_url(self):
|
|
90
|
+
return self._client_api._get_resource_url("projects")
|
|
91
|
+
|
|
92
|
+
def open_in_web(self,
|
|
93
|
+
project_name: str = None,
|
|
94
|
+
project_id: str = None,
|
|
95
|
+
project: entities.Project = None):
|
|
96
|
+
"""
|
|
97
|
+
Open the project in our web platform.
|
|
98
|
+
|
|
99
|
+
**Prerequisites**: All users can open a project in the web.
|
|
100
|
+
|
|
101
|
+
:param str project_name: The Name of the project
|
|
102
|
+
:param str project_id: The Id of the project
|
|
103
|
+
:param dtlpy.entities.project.Project project: project object
|
|
104
|
+
|
|
105
|
+
**Example**:
|
|
106
|
+
|
|
107
|
+
.. code-block:: python
|
|
108
|
+
|
|
109
|
+
dl.projects.open_in_web(project_id='project_id')
|
|
110
|
+
"""
|
|
111
|
+
if project_name is not None:
|
|
112
|
+
project = self.get(project_name=project_name)
|
|
113
|
+
if project is not None:
|
|
114
|
+
project.open_in_web()
|
|
115
|
+
elif project_id is not None:
|
|
116
|
+
self._client_api._open_in_web(url=self.platform_url + '/' + str(project_id))
|
|
117
|
+
else:
|
|
118
|
+
self._client_api._open_in_web(url=self.platform_url)
|
|
119
|
+
|
|
120
|
+
def checkout(self,
|
|
121
|
+
identifier: str = None,
|
|
122
|
+
project_name: str = None,
|
|
123
|
+
project_id: str = None,
|
|
124
|
+
project: entities.Project = None):
|
|
125
|
+
"""
|
|
126
|
+
Checkout (switch) to a project to work on.
|
|
127
|
+
|
|
128
|
+
**Prerequisites**: All users can open a project in the web.
|
|
129
|
+
|
|
130
|
+
You must provide at least ONE of the following params: project_id, project_name.
|
|
131
|
+
|
|
132
|
+
:param str identifier: project name or partial id that you wish to switch
|
|
133
|
+
:param str project_name: The Name of the project
|
|
134
|
+
:param str project_id: The Id of the project
|
|
135
|
+
:param dtlpy.entities.project.Project project: project object
|
|
136
|
+
|
|
137
|
+
**Example**:
|
|
138
|
+
|
|
139
|
+
.. code-block:: python
|
|
140
|
+
|
|
141
|
+
dl.projects.checkout(project_id='project_id')
|
|
142
|
+
"""
|
|
143
|
+
if project is None:
|
|
144
|
+
if project_id is not None or project_name is not None:
|
|
145
|
+
project = self.get(project_id=project_id, project_name=project_name)
|
|
146
|
+
elif identifier is not None:
|
|
147
|
+
project = self.__get_by_identifier(identifier=identifier)
|
|
148
|
+
else:
|
|
149
|
+
raise exceptions.PlatformException(error='400',
|
|
150
|
+
message='Must provide partial/full id/name to checkout')
|
|
151
|
+
self._client_api.state_io.put('project', project.to_json())
|
|
152
|
+
logger.info('Checked out to project {}'.format(project.name))
|
|
153
|
+
|
|
154
|
+
def _send_mail(self, project_id: str, send_to: str, title: str, content: str) -> bool:
|
|
155
|
+
if project_id:
|
|
156
|
+
url = '/projects/{}/mail'.format(project_id)
|
|
157
|
+
else:
|
|
158
|
+
url = '/outbox'
|
|
159
|
+
assert isinstance(title, str)
|
|
160
|
+
assert isinstance(content, str)
|
|
161
|
+
if self._client_api.token is not None:
|
|
162
|
+
sender = jwt.decode(self._client_api.token, algorithms=['HS256'],
|
|
163
|
+
verify=False, options={'verify_signature': False})['email']
|
|
164
|
+
else:
|
|
165
|
+
raise exceptions.PlatformException('600', 'Token expired please log in')
|
|
166
|
+
|
|
167
|
+
payload = {
|
|
168
|
+
'to': send_to,
|
|
169
|
+
'from': sender,
|
|
170
|
+
'subject': title,
|
|
171
|
+
'body': content
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
175
|
+
path=url,
|
|
176
|
+
json_req=payload)
|
|
177
|
+
|
|
178
|
+
if not success:
|
|
179
|
+
raise exceptions.PlatformException(response)
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
@_api_reference.add(path='/projects/{projectId}/members/{userId}', method='post')
|
|
183
|
+
def add_member(self, email: str, project_id: str, role: entities.MemberRole = entities.MemberRole.DEVELOPER):
|
|
184
|
+
"""
|
|
185
|
+
Add a member to the project.
|
|
186
|
+
|
|
187
|
+
**Prerequisites**: You must be in the role of an *owner* to add a member to a project.
|
|
188
|
+
|
|
189
|
+
:param str email: member email
|
|
190
|
+
:param str project_id: The Id of the project
|
|
191
|
+
:param role: The required role for the user. Use the enum dl.MemberRole
|
|
192
|
+
:return: dict that represent the user
|
|
193
|
+
:rtype: dict
|
|
194
|
+
|
|
195
|
+
**Example**:
|
|
196
|
+
|
|
197
|
+
.. code-block:: python
|
|
198
|
+
|
|
199
|
+
user_json = dl.projects.add_member(project_id='project_id', email='user@dataloop.ai', role=dl.MemberRole.DEVELOPER)
|
|
200
|
+
"""
|
|
201
|
+
url_path = '/projects/{}/members/{}'.format(project_id, email)
|
|
202
|
+
payload = dict(role=role)
|
|
203
|
+
|
|
204
|
+
if role not in list(entities.MemberRole):
|
|
205
|
+
raise ValueError('Unknown role {!r}, role must be one of: {}'.format(role,
|
|
206
|
+
', '.join(list(entities.MemberRole))))
|
|
207
|
+
|
|
208
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
209
|
+
path=url_path,
|
|
210
|
+
json_req=payload)
|
|
211
|
+
if not success:
|
|
212
|
+
raise exceptions.PlatformException(response)
|
|
213
|
+
|
|
214
|
+
return response.json()
|
|
215
|
+
|
|
216
|
+
@_api_reference.add(path='/projects/{projectId}/members/{userId}', method='patch')
|
|
217
|
+
def update_member(self, email: str, project_id: str, role: entities.MemberRole = entities.MemberRole.DEVELOPER):
|
|
218
|
+
"""
|
|
219
|
+
Update member's information/details in the project.
|
|
220
|
+
|
|
221
|
+
**Prerequisites**: You must be in the role of an *owner* to update a member.
|
|
222
|
+
|
|
223
|
+
:param str email: member email
|
|
224
|
+
:param str project_id: The Id of the project
|
|
225
|
+
:param role: The required role for the user. Use the enum dl.MemberRole
|
|
226
|
+
:return: dict that represent the user
|
|
227
|
+
:rtype: dict
|
|
228
|
+
|
|
229
|
+
**Example**:
|
|
230
|
+
|
|
231
|
+
.. code-block:: python
|
|
232
|
+
|
|
233
|
+
user_json = = dl.projects.update_member(project_id='project_id', email='user@dataloop.ai', role=dl.MemberRole.DEVELOPER)
|
|
234
|
+
"""
|
|
235
|
+
url_path = '/projects/{}/members/{}'.format(project_id, email)
|
|
236
|
+
payload = dict(role=role)
|
|
237
|
+
|
|
238
|
+
if role not in list(entities.MemberRole):
|
|
239
|
+
raise ValueError('Unknown role {!r}, role must be one of: {}'.format(role,
|
|
240
|
+
', '.join(list(entities.MemberRole))))
|
|
241
|
+
|
|
242
|
+
success, response = self._client_api.gen_request(req_type='patch',
|
|
243
|
+
path=url_path,
|
|
244
|
+
json_req=payload)
|
|
245
|
+
if not success:
|
|
246
|
+
raise exceptions.PlatformException(response)
|
|
247
|
+
|
|
248
|
+
return response.json()
|
|
249
|
+
|
|
250
|
+
@_api_reference.add(path='/projects/{projectId}/members/{userId}', method='delete')
|
|
251
|
+
def remove_member(self, email: str, project_id: str):
|
|
252
|
+
"""
|
|
253
|
+
Remove a member from the project.
|
|
254
|
+
|
|
255
|
+
**Prerequisites**: You must be in the role of an *owner* to delete a member from a project.
|
|
256
|
+
|
|
257
|
+
:param str email: member email
|
|
258
|
+
:param str project_id: The Id of the project
|
|
259
|
+
:return: dict that represents the user
|
|
260
|
+
:rtype: dict
|
|
261
|
+
|
|
262
|
+
**Example**:
|
|
263
|
+
|
|
264
|
+
.. code-block:: python
|
|
265
|
+
|
|
266
|
+
user_json = dl.projects.remove_member(project_id='project_id', email='user@dataloop.ai')
|
|
267
|
+
"""
|
|
268
|
+
url_path = '/projects/{}/members/{}'.format(project_id, email)
|
|
269
|
+
success, response = self._client_api.gen_request(req_type='delete',
|
|
270
|
+
path=url_path)
|
|
271
|
+
if not success:
|
|
272
|
+
raise exceptions.PlatformException(response)
|
|
273
|
+
|
|
274
|
+
return response.json()
|
|
275
|
+
|
|
276
|
+
@_api_reference.add(path='/projects/{projectId}/members', method='get')
|
|
277
|
+
def list_members(self, project: entities.Project, role: entities.MemberRole = None):
|
|
278
|
+
"""
|
|
279
|
+
Get a list of the project members.
|
|
280
|
+
|
|
281
|
+
**Prerequisites**: You must be in the role of an *owner* to list project members.
|
|
282
|
+
|
|
283
|
+
:param dtlpy.entities.project.Project project: Project object
|
|
284
|
+
:param role: The required role for the user. Use the enum dl.MemberRole
|
|
285
|
+
:return: list of the project members
|
|
286
|
+
:rtype: list
|
|
287
|
+
|
|
288
|
+
**Example**:
|
|
289
|
+
|
|
290
|
+
.. code-block:: python
|
|
291
|
+
|
|
292
|
+
users_jsons_list = dl.projects.list_members(project_id='project_id', role=dl.MemberRole.DEVELOPER)
|
|
293
|
+
"""
|
|
294
|
+
url_path = '/projects/{}/members'.format(project.id)
|
|
295
|
+
|
|
296
|
+
if role is not None and role not in list(entities.MemberRole):
|
|
297
|
+
raise ValueError('Unknown role {!r}, role must be one of: {}'.format(role,
|
|
298
|
+
', '.join(list(entities.MemberRole))))
|
|
299
|
+
|
|
300
|
+
success, response = self._client_api.gen_request(req_type='get',
|
|
301
|
+
path=url_path)
|
|
302
|
+
if not success:
|
|
303
|
+
raise exceptions.PlatformException(response)
|
|
304
|
+
|
|
305
|
+
members = miscellaneous.List(
|
|
306
|
+
[entities.User.from_json(_json=user, client_api=self._client_api, project=project) for user in
|
|
307
|
+
response.json()])
|
|
308
|
+
|
|
309
|
+
if role is not None:
|
|
310
|
+
members = [member for member in members if member.role == role]
|
|
311
|
+
|
|
312
|
+
return members
|
|
313
|
+
|
|
314
|
+
@_api_reference.add(path='/projects', method='get')
|
|
315
|
+
def list(self) -> miscellaneous.List[entities.Project]:
|
|
316
|
+
"""
|
|
317
|
+
Get the user's project list
|
|
318
|
+
|
|
319
|
+
**Prerequisites**: You must be a **superuser** to list all users' projects.
|
|
320
|
+
|
|
321
|
+
:return: List of Project objects
|
|
322
|
+
|
|
323
|
+
**Example**:
|
|
324
|
+
|
|
325
|
+
.. code-block:: python
|
|
326
|
+
|
|
327
|
+
projects = dl.projects.list()
|
|
328
|
+
"""
|
|
329
|
+
if self._org is None:
|
|
330
|
+
url_path = '/projects'
|
|
331
|
+
else:
|
|
332
|
+
url_path = '/orgs/{}/projects'.format(self._org.id)
|
|
333
|
+
success, response = self._client_api.gen_request(req_type='get',
|
|
334
|
+
path=url_path)
|
|
335
|
+
|
|
336
|
+
if success:
|
|
337
|
+
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
338
|
+
projects_json = response.json()
|
|
339
|
+
jobs = [None for _ in range(len(projects_json))]
|
|
340
|
+
# return triggers list
|
|
341
|
+
for i_project, project in enumerate(projects_json):
|
|
342
|
+
jobs[i_project] = pool.submit(entities.Project._protected_from_json,
|
|
343
|
+
**{'client_api': self._client_api,
|
|
344
|
+
'_json': project})
|
|
345
|
+
|
|
346
|
+
# get all results
|
|
347
|
+
results = [j.result() for j in jobs]
|
|
348
|
+
# log errors
|
|
349
|
+
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
350
|
+
# return good jobs
|
|
351
|
+
projects = miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
352
|
+
else:
|
|
353
|
+
logger.error('Platform error getting projects')
|
|
354
|
+
raise exceptions.PlatformException(response)
|
|
355
|
+
return projects
|
|
356
|
+
|
|
357
|
+
@_api_reference.add(path='/projects/{projectId}', method='get')
|
|
358
|
+
def get(self,
|
|
359
|
+
project_name: str = None,
|
|
360
|
+
project_id: str = None,
|
|
361
|
+
checkout: bool = False,
|
|
362
|
+
fetch: bool = None,
|
|
363
|
+
log_error=True) -> entities.Project:
|
|
364
|
+
"""
|
|
365
|
+
Get a Project object.
|
|
366
|
+
|
|
367
|
+
**Prerequisites**: You must be in the role of an *owner* to get a project object.
|
|
368
|
+
|
|
369
|
+
You must check out to a project or provide at least one of the following params: project_id, project_name
|
|
370
|
+
|
|
371
|
+
:param str project_name: optional - search by name
|
|
372
|
+
:param str project_id: optional - search by id
|
|
373
|
+
:param bool checkout: set the project as a default project object (cookies)
|
|
374
|
+
:param bool fetch: optional - fetch entity from platform (True), default taken from cookie
|
|
375
|
+
:param bool log_error: optional - show the logs errors
|
|
376
|
+
:return: Project object
|
|
377
|
+
:rtype: dtlpy.entities.project.Project
|
|
378
|
+
|
|
379
|
+
**Example**:
|
|
380
|
+
|
|
381
|
+
.. code-block:: python
|
|
382
|
+
|
|
383
|
+
project = dl.projects.get(project_id='project_id')
|
|
384
|
+
"""
|
|
385
|
+
if fetch is None:
|
|
386
|
+
fetch = self._client_api.fetch_entities
|
|
387
|
+
|
|
388
|
+
if project_id is None and project_name is None:
|
|
389
|
+
project = self.__get_from_cache()
|
|
390
|
+
if project is None:
|
|
391
|
+
raise exceptions.PlatformException(
|
|
392
|
+
error='400',
|
|
393
|
+
message='No checked-out Project was found. You must checkout to a project or provide an identifier in inputs')
|
|
394
|
+
elif fetch:
|
|
395
|
+
if project_id is not None:
|
|
396
|
+
if not isinstance(project_id, str):
|
|
397
|
+
raise exceptions.PlatformException(
|
|
398
|
+
error='400',
|
|
399
|
+
message='project_id must be strings')
|
|
400
|
+
|
|
401
|
+
project = self.__get_by_id(project_id, log_error=log_error)
|
|
402
|
+
# verify input project name is same as the given id
|
|
403
|
+
if project_name is not None and project.name != project_name:
|
|
404
|
+
logger.warning(
|
|
405
|
+
"Mismatch found in projects.get: project_name is different then project.name:"
|
|
406
|
+
" {!r} != {!r}".format(
|
|
407
|
+
project_name,
|
|
408
|
+
project.name))
|
|
409
|
+
elif project_name is not None:
|
|
410
|
+
if not isinstance(project_name, str):
|
|
411
|
+
raise exceptions.PlatformException(
|
|
412
|
+
error='400',
|
|
413
|
+
message='project_name must be strings')
|
|
414
|
+
|
|
415
|
+
projects = self.__get_by_name(project_name)
|
|
416
|
+
if len(projects) > 1:
|
|
417
|
+
# more than one matching project
|
|
418
|
+
raise exceptions.PlatformException(
|
|
419
|
+
error='404',
|
|
420
|
+
message='More than one project with same name. Please "get" by id')
|
|
421
|
+
else:
|
|
422
|
+
project = projects[0]
|
|
423
|
+
else:
|
|
424
|
+
raise exceptions.PlatformException(
|
|
425
|
+
error='404',
|
|
426
|
+
message='No input and no checked-out found')
|
|
427
|
+
else:
|
|
428
|
+
project = entities.Project.from_json(_json={'id': project_id,
|
|
429
|
+
'name': project_name},
|
|
430
|
+
client_api=self._client_api,
|
|
431
|
+
is_fetched=False)
|
|
432
|
+
assert isinstance(project, entities.Project)
|
|
433
|
+
if checkout:
|
|
434
|
+
self.checkout(project=project)
|
|
435
|
+
return project
|
|
436
|
+
|
|
437
|
+
@_api_reference.add(path='/projects/{projectId}', method='delete')
|
|
438
|
+
def delete(self,
|
|
439
|
+
project_name: str = None,
|
|
440
|
+
project_id: str = None,
|
|
441
|
+
sure: bool = False,
|
|
442
|
+
really: bool = False) -> bool:
|
|
443
|
+
"""
|
|
444
|
+
Delete a project forever!
|
|
445
|
+
|
|
446
|
+
**Prerequisites**: You must be in the role of an *owner* to delete a project.
|
|
447
|
+
|
|
448
|
+
:param str project_name: optional - search by name
|
|
449
|
+
:param str project_id: optional - search by id
|
|
450
|
+
:param bool sure: Are you sure you want to delete?
|
|
451
|
+
:param bool really: Really really sure?
|
|
452
|
+
:return: True if success, error if not
|
|
453
|
+
:rtype: bool
|
|
454
|
+
|
|
455
|
+
**Example**:
|
|
456
|
+
|
|
457
|
+
.. code-block:: python
|
|
458
|
+
|
|
459
|
+
is_deleted = dl.projects.delete(project_id='project_id', sure=True, really=True)
|
|
460
|
+
"""
|
|
461
|
+
if sure and really:
|
|
462
|
+
if project_id is None:
|
|
463
|
+
project = self.get(project_name=project_name)
|
|
464
|
+
project_id = project.id
|
|
465
|
+
success, response = self._client_api.gen_request(req_type='delete',
|
|
466
|
+
path='/projects/{}'.format(project_id))
|
|
467
|
+
if not success:
|
|
468
|
+
raise exceptions.PlatformException(response)
|
|
469
|
+
logger.info('Project id {} deleted successfully'.format(project_id))
|
|
470
|
+
return True
|
|
471
|
+
else:
|
|
472
|
+
raise exceptions.PlatformException(
|
|
473
|
+
error='403',
|
|
474
|
+
message='Cant delete project from SDK. Please login to platform to delete')
|
|
475
|
+
|
|
476
|
+
@_api_reference.add(path='/projects/{projectId}', method='patch')
|
|
477
|
+
def update(self,
|
|
478
|
+
project: entities.Project,
|
|
479
|
+
system_metadata: bool = False) -> entities.Project:
|
|
480
|
+
"""
|
|
481
|
+
Update a project information (e.g., name, member roles, etc.).
|
|
482
|
+
|
|
483
|
+
**Prerequisites**: You must be in the role of an *owner* to add a member to a project.
|
|
484
|
+
|
|
485
|
+
:param dtlpy.entities.project.Project project: project object
|
|
486
|
+
:param bool system_metadata: optional - True, if you want to change metadata system
|
|
487
|
+
:return: Project object
|
|
488
|
+
:rtype: dtlpy.entities.project.Project
|
|
489
|
+
|
|
490
|
+
**Example**:
|
|
491
|
+
|
|
492
|
+
.. code-block:: python
|
|
493
|
+
|
|
494
|
+
project = dl.projects.delete(project='project_entity')
|
|
495
|
+
"""
|
|
496
|
+
url_path = '/projects/{}'.format(project.id)
|
|
497
|
+
if system_metadata:
|
|
498
|
+
url_path += '?system=true'
|
|
499
|
+
success, response = self._client_api.gen_request(req_type='patch',
|
|
500
|
+
path=url_path,
|
|
501
|
+
json_req=project.to_json())
|
|
502
|
+
if success:
|
|
503
|
+
return project
|
|
504
|
+
else:
|
|
505
|
+
raise exceptions.PlatformException(response)
|
|
506
|
+
|
|
507
|
+
@_api_reference.add(path='/projects', method='post')
|
|
508
|
+
def create(self,
|
|
509
|
+
project_name: str,
|
|
510
|
+
checkout: bool = False) -> entities.Project:
|
|
511
|
+
"""
|
|
512
|
+
Create a new project.
|
|
513
|
+
|
|
514
|
+
**Prerequisites**: Any user can create a project.
|
|
515
|
+
|
|
516
|
+
:param str project_name: The Name of the project
|
|
517
|
+
:param bool checkout: set the project as a default project object (cookies)
|
|
518
|
+
:return: Project object
|
|
519
|
+
:rtype: dtlpy.entities.project.Project
|
|
520
|
+
|
|
521
|
+
**Example**:
|
|
522
|
+
|
|
523
|
+
.. code-block:: python
|
|
524
|
+
|
|
525
|
+
project = dl.projects.create(project_name='project_name')
|
|
526
|
+
"""
|
|
527
|
+
payload = {'name': project_name}
|
|
528
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
529
|
+
path='/projects',
|
|
530
|
+
data=payload)
|
|
531
|
+
if success:
|
|
532
|
+
project = entities.Project.from_json(client_api=self._client_api,
|
|
533
|
+
_json=response.json())
|
|
534
|
+
else:
|
|
535
|
+
raise exceptions.PlatformException(response)
|
|
536
|
+
assert isinstance(project, entities.Project)
|
|
537
|
+
if checkout:
|
|
538
|
+
self.checkout(project=project)
|
|
539
|
+
return project
|