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