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.
Files changed (243) hide show
  1. dtlpy/__init__.py +488 -488
  2. dtlpy/__version__.py +1 -1
  3. dtlpy/assets/__init__.py +26 -26
  4. dtlpy/assets/__pycache__/__init__.cpython-38.pyc +0 -0
  5. dtlpy/assets/code_server/config.yaml +2 -2
  6. dtlpy/assets/code_server/installation.sh +24 -24
  7. dtlpy/assets/code_server/launch.json +13 -13
  8. dtlpy/assets/code_server/settings.json +2 -2
  9. dtlpy/assets/main.py +53 -53
  10. dtlpy/assets/main_partial.py +18 -18
  11. dtlpy/assets/mock.json +11 -11
  12. dtlpy/assets/model_adapter.py +83 -83
  13. dtlpy/assets/package.json +61 -61
  14. dtlpy/assets/package_catalog.json +29 -29
  15. dtlpy/assets/package_gitignore +307 -307
  16. dtlpy/assets/service_runners/__init__.py +33 -33
  17. dtlpy/assets/service_runners/converter.py +96 -96
  18. dtlpy/assets/service_runners/multi_method.py +49 -49
  19. dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
  20. dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
  21. dtlpy/assets/service_runners/multi_method_item.py +52 -52
  22. dtlpy/assets/service_runners/multi_method_json.py +52 -52
  23. dtlpy/assets/service_runners/single_method.py +37 -37
  24. dtlpy/assets/service_runners/single_method_annotation.py +43 -43
  25. dtlpy/assets/service_runners/single_method_dataset.py +43 -43
  26. dtlpy/assets/service_runners/single_method_item.py +41 -41
  27. dtlpy/assets/service_runners/single_method_json.py +42 -42
  28. dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
  29. dtlpy/assets/voc_annotation_template.xml +23 -23
  30. dtlpy/caches/base_cache.py +32 -32
  31. dtlpy/caches/cache.py +473 -473
  32. dtlpy/caches/dl_cache.py +201 -201
  33. dtlpy/caches/filesystem_cache.py +89 -89
  34. dtlpy/caches/redis_cache.py +84 -84
  35. dtlpy/dlp/__init__.py +20 -20
  36. dtlpy/dlp/cli_utilities.py +367 -367
  37. dtlpy/dlp/command_executor.py +764 -764
  38. dtlpy/dlp/dlp +1 -1
  39. dtlpy/dlp/dlp.bat +1 -1
  40. dtlpy/dlp/dlp.py +128 -128
  41. dtlpy/dlp/parser.py +651 -651
  42. dtlpy/entities/__init__.py +83 -83
  43. dtlpy/entities/analytic.py +311 -311
  44. dtlpy/entities/annotation.py +1879 -1879
  45. dtlpy/entities/annotation_collection.py +699 -699
  46. dtlpy/entities/annotation_definitions/__init__.py +20 -20
  47. dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
  48. dtlpy/entities/annotation_definitions/box.py +195 -195
  49. dtlpy/entities/annotation_definitions/classification.py +67 -67
  50. dtlpy/entities/annotation_definitions/comparison.py +72 -72
  51. dtlpy/entities/annotation_definitions/cube.py +204 -204
  52. dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
  53. dtlpy/entities/annotation_definitions/description.py +32 -32
  54. dtlpy/entities/annotation_definitions/ellipse.py +124 -124
  55. dtlpy/entities/annotation_definitions/free_text.py +62 -62
  56. dtlpy/entities/annotation_definitions/gis.py +69 -69
  57. dtlpy/entities/annotation_definitions/note.py +139 -139
  58. dtlpy/entities/annotation_definitions/point.py +117 -117
  59. dtlpy/entities/annotation_definitions/polygon.py +182 -182
  60. dtlpy/entities/annotation_definitions/polyline.py +111 -111
  61. dtlpy/entities/annotation_definitions/pose.py +92 -92
  62. dtlpy/entities/annotation_definitions/ref_image.py +86 -86
  63. dtlpy/entities/annotation_definitions/segmentation.py +240 -240
  64. dtlpy/entities/annotation_definitions/subtitle.py +34 -34
  65. dtlpy/entities/annotation_definitions/text.py +85 -85
  66. dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
  67. dtlpy/entities/app.py +220 -220
  68. dtlpy/entities/app_module.py +107 -107
  69. dtlpy/entities/artifact.py +174 -174
  70. dtlpy/entities/assignment.py +399 -399
  71. dtlpy/entities/base_entity.py +214 -214
  72. dtlpy/entities/bot.py +113 -113
  73. dtlpy/entities/codebase.py +296 -296
  74. dtlpy/entities/collection.py +38 -38
  75. dtlpy/entities/command.py +169 -169
  76. dtlpy/entities/compute.py +442 -442
  77. dtlpy/entities/dataset.py +1285 -1285
  78. dtlpy/entities/directory_tree.py +44 -44
  79. dtlpy/entities/dpk.py +470 -470
  80. dtlpy/entities/driver.py +222 -222
  81. dtlpy/entities/execution.py +397 -397
  82. dtlpy/entities/feature.py +124 -124
  83. dtlpy/entities/feature_set.py +145 -145
  84. dtlpy/entities/filters.py +641 -641
  85. dtlpy/entities/gis_item.py +107 -107
  86. dtlpy/entities/integration.py +184 -184
  87. dtlpy/entities/item.py +953 -953
  88. dtlpy/entities/label.py +123 -123
  89. dtlpy/entities/links.py +85 -85
  90. dtlpy/entities/message.py +175 -175
  91. dtlpy/entities/model.py +694 -691
  92. dtlpy/entities/node.py +1005 -1005
  93. dtlpy/entities/ontology.py +803 -803
  94. dtlpy/entities/organization.py +287 -287
  95. dtlpy/entities/package.py +657 -657
  96. dtlpy/entities/package_defaults.py +5 -5
  97. dtlpy/entities/package_function.py +185 -185
  98. dtlpy/entities/package_module.py +113 -113
  99. dtlpy/entities/package_slot.py +118 -118
  100. dtlpy/entities/paged_entities.py +290 -267
  101. dtlpy/entities/pipeline.py +593 -593
  102. dtlpy/entities/pipeline_execution.py +279 -279
  103. dtlpy/entities/project.py +394 -394
  104. dtlpy/entities/prompt_item.py +499 -499
  105. dtlpy/entities/recipe.py +301 -301
  106. dtlpy/entities/reflect_dict.py +102 -102
  107. dtlpy/entities/resource_execution.py +138 -138
  108. dtlpy/entities/service.py +958 -958
  109. dtlpy/entities/service_driver.py +117 -117
  110. dtlpy/entities/setting.py +294 -294
  111. dtlpy/entities/task.py +491 -491
  112. dtlpy/entities/time_series.py +143 -143
  113. dtlpy/entities/trigger.py +426 -426
  114. dtlpy/entities/user.py +118 -118
  115. dtlpy/entities/webhook.py +124 -124
  116. dtlpy/examples/__init__.py +19 -19
  117. dtlpy/examples/add_labels.py +135 -135
  118. dtlpy/examples/add_metadata_to_item.py +21 -21
  119. dtlpy/examples/annotate_items_using_model.py +65 -65
  120. dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
  121. dtlpy/examples/annotations_convert_to_voc.py +9 -9
  122. dtlpy/examples/annotations_convert_to_yolo.py +9 -9
  123. dtlpy/examples/convert_annotation_types.py +51 -51
  124. dtlpy/examples/converter.py +143 -143
  125. dtlpy/examples/copy_annotations.py +22 -22
  126. dtlpy/examples/copy_folder.py +31 -31
  127. dtlpy/examples/create_annotations.py +51 -51
  128. dtlpy/examples/create_video_annotations.py +83 -83
  129. dtlpy/examples/delete_annotations.py +26 -26
  130. dtlpy/examples/filters.py +113 -113
  131. dtlpy/examples/move_item.py +23 -23
  132. dtlpy/examples/play_video_annotation.py +13 -13
  133. dtlpy/examples/show_item_and_mask.py +53 -53
  134. dtlpy/examples/triggers.py +49 -49
  135. dtlpy/examples/upload_batch_of_items.py +20 -20
  136. dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
  137. dtlpy/examples/upload_items_with_modalities.py +43 -43
  138. dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
  139. dtlpy/examples/upload_yolo_format_annotations.py +70 -70
  140. dtlpy/exceptions.py +125 -125
  141. dtlpy/miscellaneous/__init__.py +20 -20
  142. dtlpy/miscellaneous/dict_differ.py +95 -95
  143. dtlpy/miscellaneous/git_utils.py +217 -217
  144. dtlpy/miscellaneous/json_utils.py +14 -14
  145. dtlpy/miscellaneous/list_print.py +105 -105
  146. dtlpy/miscellaneous/zipping.py +130 -130
  147. dtlpy/ml/__init__.py +20 -20
  148. dtlpy/ml/base_feature_extractor_adapter.py +27 -27
  149. dtlpy/ml/base_model_adapter.py +945 -940
  150. dtlpy/ml/metrics.py +461 -461
  151. dtlpy/ml/predictions_utils.py +274 -274
  152. dtlpy/ml/summary_writer.py +57 -57
  153. dtlpy/ml/train_utils.py +60 -60
  154. dtlpy/new_instance.py +252 -252
  155. dtlpy/repositories/__init__.py +56 -56
  156. dtlpy/repositories/analytics.py +85 -85
  157. dtlpy/repositories/annotations.py +916 -916
  158. dtlpy/repositories/apps.py +383 -383
  159. dtlpy/repositories/artifacts.py +452 -452
  160. dtlpy/repositories/assignments.py +599 -599
  161. dtlpy/repositories/bots.py +213 -213
  162. dtlpy/repositories/codebases.py +559 -559
  163. dtlpy/repositories/collections.py +332 -348
  164. dtlpy/repositories/commands.py +158 -158
  165. dtlpy/repositories/compositions.py +61 -61
  166. dtlpy/repositories/computes.py +434 -406
  167. dtlpy/repositories/datasets.py +1291 -1291
  168. dtlpy/repositories/downloader.py +895 -895
  169. dtlpy/repositories/dpks.py +433 -433
  170. dtlpy/repositories/drivers.py +266 -266
  171. dtlpy/repositories/executions.py +817 -817
  172. dtlpy/repositories/feature_sets.py +226 -226
  173. dtlpy/repositories/features.py +238 -238
  174. dtlpy/repositories/integrations.py +484 -484
  175. dtlpy/repositories/items.py +909 -915
  176. dtlpy/repositories/messages.py +94 -94
  177. dtlpy/repositories/models.py +877 -867
  178. dtlpy/repositories/nodes.py +80 -80
  179. dtlpy/repositories/ontologies.py +511 -511
  180. dtlpy/repositories/organizations.py +525 -525
  181. dtlpy/repositories/packages.py +1941 -1941
  182. dtlpy/repositories/pipeline_executions.py +448 -448
  183. dtlpy/repositories/pipelines.py +642 -642
  184. dtlpy/repositories/projects.py +539 -539
  185. dtlpy/repositories/recipes.py +399 -399
  186. dtlpy/repositories/resource_executions.py +137 -137
  187. dtlpy/repositories/schema.py +120 -120
  188. dtlpy/repositories/service_drivers.py +213 -213
  189. dtlpy/repositories/services.py +1704 -1704
  190. dtlpy/repositories/settings.py +339 -339
  191. dtlpy/repositories/tasks.py +1124 -1124
  192. dtlpy/repositories/times_series.py +278 -278
  193. dtlpy/repositories/triggers.py +536 -536
  194. dtlpy/repositories/upload_element.py +257 -257
  195. dtlpy/repositories/uploader.py +651 -651
  196. dtlpy/repositories/webhooks.py +249 -249
  197. dtlpy/services/__init__.py +22 -22
  198. dtlpy/services/aihttp_retry.py +131 -131
  199. dtlpy/services/api_client.py +1782 -1782
  200. dtlpy/services/api_reference.py +40 -40
  201. dtlpy/services/async_utils.py +133 -133
  202. dtlpy/services/calls_counter.py +44 -44
  203. dtlpy/services/check_sdk.py +68 -68
  204. dtlpy/services/cookie.py +115 -115
  205. dtlpy/services/create_logger.py +156 -156
  206. dtlpy/services/events.py +84 -84
  207. dtlpy/services/logins.py +235 -235
  208. dtlpy/services/reporter.py +256 -256
  209. dtlpy/services/service_defaults.py +91 -91
  210. dtlpy/utilities/__init__.py +20 -20
  211. dtlpy/utilities/annotations/__init__.py +16 -16
  212. dtlpy/utilities/annotations/annotation_converters.py +269 -269
  213. dtlpy/utilities/base_package_runner.py +264 -264
  214. dtlpy/utilities/converter.py +1650 -1650
  215. dtlpy/utilities/dataset_generators/__init__.py +1 -1
  216. dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
  217. dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
  218. dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
  219. dtlpy/utilities/local_development/__init__.py +1 -1
  220. dtlpy/utilities/local_development/local_session.py +179 -179
  221. dtlpy/utilities/reports/__init__.py +2 -2
  222. dtlpy/utilities/reports/figures.py +343 -343
  223. dtlpy/utilities/reports/report.py +71 -71
  224. dtlpy/utilities/videos/__init__.py +17 -17
  225. dtlpy/utilities/videos/video_player.py +598 -598
  226. dtlpy/utilities/videos/videos.py +470 -470
  227. {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp +1 -1
  228. dtlpy-1.114.13.data/scripts/dlp.bat +2 -0
  229. {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp.py +128 -128
  230. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/LICENSE +200 -200
  231. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/METADATA +172 -172
  232. dtlpy-1.114.13.dist-info/RECORD +240 -0
  233. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/WHEEL +1 -1
  234. tests/features/environment.py +551 -550
  235. dtlpy-1.113.10.data/scripts/dlp.bat +0 -2
  236. dtlpy-1.113.10.dist-info/RECORD +0 -244
  237. tests/assets/__init__.py +0 -0
  238. tests/assets/models_flow/__init__.py +0 -0
  239. tests/assets/models_flow/failedmain.py +0 -52
  240. tests/assets/models_flow/main.py +0 -62
  241. tests/assets/models_flow/main_model.py +0 -54
  242. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/entry_points.txt +0 -0
  243. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/top_level.txt +0 -0
@@ -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