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,599 +1,599 @@
1
- import logging
2
-
3
- from .. import exceptions, miscellaneous, entities, repositories, _api_reference
4
- from ..services.api_client import ApiClient
5
-
6
- logger = logging.getLogger(name='dtlpy')
7
-
8
-
9
- class Assignments:
10
- """
11
- Assignments Repository
12
-
13
- The Assignments class allows users to manage assignments and their properties.
14
- Read more about `Task Assignment <https://developers.dataloop.ai/tutorials/task_workflows/create_a_task/chapter/>`_ in our Developers documentation.
15
- """
16
-
17
- def __init__(self,
18
- client_api: ApiClient,
19
- project: entities.Project = None,
20
- task: entities.Task = None,
21
- dataset: entities.Dataset = None,
22
- project_id=None):
23
- self._client_api = client_api
24
- self._project = project
25
- self._dataset = dataset
26
- self._task = task
27
-
28
- self._project_id = project_id
29
- if self._project_id is None and self._project is not None:
30
- self._project_id = self._project.id
31
-
32
- ############
33
- # entities #
34
- ############
35
- @property
36
- def task(self) -> entities.Task:
37
- if self._task is None:
38
- raise exceptions.PlatformException(
39
- error='2001',
40
- message='Missing "task". need to set an Task entity or use task.assignments repository')
41
- assert isinstance(self._task, entities.Task)
42
- return self._task
43
-
44
- @task.setter
45
- def task(self, task: entities.Task):
46
- if not isinstance(task, entities.Task):
47
- raise ValueError('Must input a valid Task entity')
48
- self._task = task
49
-
50
- @property
51
- def project_id(self):
52
- if self._project_id is not None:
53
- return self._project_id
54
- elif self._project is not None:
55
- return self._project.id
56
- else:
57
- return None
58
-
59
- @property
60
- def project(self) -> entities.Project:
61
- if self._project is None:
62
- raise exceptions.PlatformException(
63
- error='2001',
64
- message='Missing "project". need to set a Project entity or use project.assignments repository')
65
- assert isinstance(self._project, entities.Project)
66
- return self._project
67
-
68
- @project.setter
69
- def project(self, project: entities.Project):
70
- if not isinstance(project, entities.Project):
71
- raise ValueError('Must input a valid Project entity')
72
- self._project = project
73
-
74
- ###########
75
- # methods #
76
- ###########
77
- def list(self,
78
- project_ids: list = None,
79
- status: str = None,
80
- assignment_name: str = None,
81
- assignee_id: str = None,
82
- pages_size: int = None,
83
- page_offset: int = None,
84
- task_id: int = None
85
- ) -> miscellaneous.List[entities.Assignment]:
86
- """
87
- Get Assignment list to be able to use it in your code.
88
-
89
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
90
-
91
- :param list project_ids: search assignment by given list of project ids
92
- :param str status: search assignment by a given task status
93
- :param str assignment_name: search assignment by a given assignment name
94
- :param str assignee_id: the user email that assignee the assignment to it
95
- :param int pages_size: pages size of the output generator
96
- :param int page_offset: page offset of the output generator
97
- :param str task_id: search assignment by given task id
98
- :return: List of Assignment objects
99
- :rtype: miscellaneous.List[dtlpy.entities.assignment.Assignment]
100
-
101
- **Example**:
102
-
103
- .. code-block:: python
104
-
105
- assignments = task.assignments.list(status='complete', assignee_id='user@dataloop.ai', pages_size=100, page_offset=0)
106
- """
107
-
108
- # url
109
- url = '/assignments'
110
-
111
- query = list()
112
- if project_ids is not None:
113
- if not isinstance(project_ids, list):
114
- project_ids = [project_ids]
115
- elif self._project_id is not None:
116
- project_ids = [self._project_id]
117
- elif self._project is not None:
118
- project_ids = [self._project.id]
119
- else:
120
- raise exceptions.PlatformException(error='400', message='Must provide project')
121
-
122
- project_ids = ','.join(project_ids)
123
- query.append('projects={}'.format(project_ids))
124
-
125
- if status is not None:
126
- query.append('status={}'.format(status))
127
- if assignment_name is not None:
128
- query.append('name={}'.format(assignment_name))
129
- if assignee_id is not None:
130
- query.append('annotator={}'.format(assignee_id))
131
- if pages_size is not None:
132
- query.append('pageSize={}'.format(pages_size))
133
- if pages_size is None:
134
- query.append('pageSize={}'.format(500))
135
- if page_offset is not None:
136
- query.append('pageOffset={}'.format(page_offset))
137
-
138
- if task_id is None and self._task is not None:
139
- task_id = self._task.id
140
- if task_id is not None:
141
- query.append('taskId={}'.format(task_id))
142
-
143
- if len(query) > 0:
144
- query_string = '&'.join(query)
145
- url = '{}?{}'.format(url, query_string)
146
-
147
- success, response = self._client_api.gen_request(req_type='get',
148
- path=url)
149
- if success:
150
- assignments = miscellaneous.List(
151
- [entities.Assignment.from_json(client_api=self._client_api,
152
- _json=_json, project=self._project, dataset=self._dataset,
153
- task=self._task)
154
- for _json in response.json()['items']])
155
- else:
156
- logger.error('Platform error getting assignments')
157
- raise exceptions.PlatformException(response)
158
- return assignments
159
-
160
- @_api_reference.add(path='/assignments/{id}', method='get')
161
- def get(self,
162
- assignment_name: str = None,
163
- assignment_id: str = None):
164
- """
165
- Get Assignment object to use it in your code.
166
-
167
- :param str assignment_name: optional - search by name
168
- :param str assignment_id: optional - search by id
169
- :return: Assignment object
170
- :rtype: dtlpy.entities.assignment.Assignment
171
-
172
- **Example**:
173
-
174
- .. code-block:: python
175
-
176
- assignment = task.assignments.get(assignment_id='assignment_id')
177
- """
178
-
179
- if assignment_id is not None:
180
- url = '/assignments/{}'.format(assignment_id)
181
- success, response = self._client_api.gen_request(req_type='get',
182
- path=url)
183
- if not success:
184
- raise exceptions.PlatformException('404', 'Assignment not found')
185
- else:
186
- assignment = entities.Assignment.from_json(_json=response.json(),
187
- client_api=self._client_api,
188
- project=self._project,
189
- dataset=self._dataset,
190
- task=self._task)
191
- # verify input assignment name is same as the given id
192
- if assignment_name is not None and assignment.name != assignment_name:
193
- logger.warning(
194
- "Mismatch found in assignments.get: assignment_name is different then assignment.name: "
195
- "{!r} != {!r}".format(
196
- assignment_name,
197
- assignment.name))
198
- elif assignment_name is not None:
199
- assignments = [assignment for assignment in self.list() if assignment.name == assignment_name]
200
- if len(assignments) == 0:
201
- raise exceptions.PlatformException('404', 'Assignment not found')
202
- elif len(assignments) > 1:
203
- raise exceptions.PlatformException('404',
204
- 'More than one assignment exist with the same name: {}'.format(
205
- assignment_name))
206
- else:
207
- assignment = assignments[0]
208
- else:
209
- raise exceptions.PlatformException('400', 'Must provide either assignment name or assignment id')
210
-
211
- assert isinstance(assignment, entities.Assignment)
212
- return assignment
213
-
214
- @property
215
- def platform_url(self):
216
- if self.task.id is None or self.project_id is None:
217
- raise ValueError("must have project and task")
218
-
219
- return self._client_api._get_resource_url(
220
- "projects/{}/tasks/{}/assignments".format(self.project_id, self.task.id))
221
-
222
- def open_in_web(self,
223
- assignment_name: str = None,
224
- assignment_id: str = None,
225
- assignment: str = None):
226
- """
227
- Open the assignment in the platform.
228
-
229
- **Prerequisites**: All users.
230
-
231
- :param str assignment_name: the name of the assignment
232
- :param str assignment_id: the Id of the assignment
233
- :param dtlpy.entities.assignment.Assignment assignment: assignment object
234
-
235
- **Example**:
236
-
237
- .. code-block:: python
238
-
239
- task.assignments.open_in_web(assignment_id='assignment_id')
240
- """
241
- if assignment_name is not None:
242
- assignment = self.get(assignment_name=assignment_name)
243
- if assignment is not None:
244
- assignment.open_in_web()
245
- elif assignment_id is not None:
246
- self._client_api._open_in_web(url=self.platform_url + '/' + str(assignment_id))
247
- else:
248
- self._client_api._open_in_web(url=self.platform_url)
249
-
250
- @_api_reference.add(path='/assignments/{id}/reassign', method='post')
251
- def reassign(self,
252
- assignee_id: str,
253
- assignment: entities.Assignment = None,
254
- assignment_id: str = None,
255
- task: entities.Task = None,
256
- task_id: str = None,
257
- wait: bool = True):
258
- """
259
- Reassign an assignment.
260
-
261
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
262
-
263
- :param str assignee_id: the email of the user that want to assign the assignment
264
- :param dtlpy.entities.assignment.Assignment assignment: assignment object
265
- :param assignment_id: the Id of the assignment
266
- :param dtlpy.entities.task.Task task: task object
267
- :param str task_id: the Id of the task that include the assignment
268
- :param bool wait: wait until reassign assignment finish
269
- :return: Assignment object
270
- :rtype: dtlpy.entities.assignment.Assignment
271
-
272
- **Example**:
273
-
274
- .. code-block:: python
275
-
276
- assignment = task.assignments.reassign(assignee_ids='annotator1@dataloop.ai')
277
- """
278
- if assignment_id is None and assignment is None:
279
- raise exceptions.PlatformException('400', 'Must provide either assignment or assignment_id')
280
- elif assignment_id is None:
281
- assignment_id = assignment.id
282
-
283
- if task_id is None and task is None:
284
- raise exceptions.PlatformException('400', 'Must provide either task or task_id')
285
- elif task_id is None:
286
- task_id = task.id
287
-
288
- url = '/assignments/{}/reassign'.format(assignment_id)
289
-
290
- payload = {
291
- 'taskId': task_id,
292
- 'annotator': assignee_id,
293
- 'asynced': wait
294
- }
295
-
296
- success, response = self._client_api.gen_request(req_type='post',
297
- path=url,
298
- json_req=payload)
299
- if success:
300
- command = entities.Command.from_json(_json=response.json(),
301
- client_api=self._client_api)
302
- if not wait:
303
- return command
304
- command = command.wait(timeout=0)
305
- if 'toAssignment' not in command.spec:
306
- raise exceptions.PlatformException(error='400',
307
- message="'toAssignment' key is missing in command response: {}"
308
- .format(response))
309
- return self.get(assignment_id=command.spec['toAssignment'])
310
- else:
311
- raise exceptions.PlatformException(response)
312
-
313
- @_api_reference.add(path='/assignments/{id}/redistribute', method='post')
314
- def redistribute(self,
315
- workload: entities.Workload,
316
- assignment: entities.Assignment = None,
317
- assignment_id: str = None,
318
- task: entities.Task = None,
319
- task_id: str = None,
320
- wait: bool = True):
321
- """
322
- Redistribute an assignment.
323
-
324
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
325
-
326
- **Example**:
327
-
328
- :param dtlpy.entities.assignment.Workload workload: list of WorkloadUnit objects. Customize distribution (percentage) between the task assignees. For example: [dl.WorkloadUnit(annotator@hi.com, 80), dl.WorkloadUnit(annotator2@hi.com, 20)]
329
- :param dtlpy.entities.assignment.Assignment assignment: assignment object
330
- :param str assignment_id: the Id of the assignment
331
- :param dtlpy.entities.task.Task task: the task object that include the assignment
332
- :param str task_id: the Id of the task that include the assignment
333
- :param bool wait: wait until redistribute assignment finish
334
- :return: Assignment object
335
- :rtype: dtlpy.entities.assignment.Assignment assignment
336
-
337
- .. code-block:: python
338
-
339
- assignment = task.assignments.redistribute(workload=dl.Workload([dl.WorkloadUnit(assignee_id="annotator1@dataloop.ai", load=50),
340
- dl.WorkloadUnit(assignee_id="annotator2@dataloop.ai", load=50)]))
341
- """
342
- if assignment_id is None and assignment is None:
343
- raise exceptions.PlatformException('400', 'Must provide either assignment or assignment_id')
344
- elif assignment_id is None:
345
- assignment_id = assignment.id
346
-
347
- if task_id is None and task is None:
348
- raise exceptions.PlatformException('400', 'Must provide either task or task_id')
349
- elif task_id is None:
350
- task_id = task.id
351
-
352
- url = '/assignments/{}/redistribute'.format(assignment_id)
353
-
354
- payload = {
355
- 'taskId': task_id,
356
- 'workload': workload.to_json(),
357
- 'asynced': wait
358
- }
359
-
360
- if self._task is None:
361
- self._task = self.get(assignment_id=assignment_id).task
362
- if task is None:
363
- task = self._task
364
-
365
- success, response = self._client_api.gen_request(req_type='post',
366
- path=url,
367
- json_req=payload)
368
- if success:
369
- command = entities.Command.from_json(_json=response.json(),
370
- client_api=self._client_api)
371
- if not wait:
372
- return command
373
- command = command.wait(timeout=0)
374
- if 'workload' not in command.spec:
375
- raise exceptions.PlatformException(error='400',
376
- message="workload key is missing in command response: {}"
377
- .format(response))
378
-
379
- task_assignments = task.assignments.list()
380
- workers = list()
381
- for worker in workload:
382
- workers.append(worker.assignee_id.lower())
383
-
384
- redistributed_assignments = list()
385
- for ass in task_assignments:
386
- if ass.annotator in workers:
387
- redistributed_assignments.append(ass)
388
- workers.remove(ass.annotator)
389
- if not workers:
390
- break
391
-
392
- return miscellaneous.List(redistributed_assignments)
393
- else:
394
- raise exceptions.PlatformException(response)
395
-
396
- @_api_reference.add(path='/assignments/{id}', method='patch')
397
- def update(self,
398
- assignment: entities.Assignment = None,
399
- system_metadata: bool = False
400
- ) -> entities.Assignment:
401
- """
402
- Update an assignment.
403
-
404
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
405
-
406
- :param dtlpy.entities.assignment.Assignment assignment assignment: assignment entity
407
- :param bool system_metadata: True, if you want to change metadata system
408
- :return: Assignment object
409
- :rtype: dtlpy.entities.assignment.Assignment assignment
410
-
411
- **Example**:
412
-
413
- .. code-block:: python
414
-
415
- assignment = task.assignments.update(assignment='assignment_entity', system_metadata=False)
416
- """
417
- url = '/assignments/{}'.format(assignment.id)
418
-
419
- if system_metadata:
420
- url += '?system=true'
421
-
422
- success, response = self._client_api.gen_request(req_type='patch',
423
- path=url,
424
- json_req=assignment.to_json())
425
- if success:
426
- return entities.Assignment.from_json(_json=response.json(),
427
- client_api=self._client_api, project=self._project,
428
- dataset=self._dataset, task=self._task)
429
- else:
430
- raise exceptions.PlatformException(response)
431
-
432
- def create(self,
433
- assignee_id: str,
434
- task: entities.Task = None,
435
- filters: entities.Filters = None,
436
- items: list = None) -> entities.Assignment:
437
- """
438
- Create a new assignment.
439
-
440
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
441
-
442
- :param str assignee_id: the email of the user that want to assign the assignment
443
- :param dtlpy.entities.task.Task task: the task object that include the assignment
444
- :param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
445
- :param list items: list of items (item Id or objects) to insert to the assignment
446
- :return: Assignment object
447
- :rtype: dtlpy.entities.assignment.Assignment assignment
448
-
449
- **Example**:
450
-
451
- .. code-block:: python
452
-
453
- assignment = task.assignments.create(assignee_id='annotator1@dataloop.ai')
454
- """
455
- return self._create_in_task(assignee_id=assignee_id, task=task, filters=filters, items=items)
456
-
457
- def _create_in_task(self, assignee_id, task, filters=None, items=None) -> entities.Assignment:
458
-
459
- if task is None:
460
- if self._task is None:
461
- raise exceptions.PlatformException('400', 'Must provide task')
462
- task = self._task
463
-
464
- assignments_before = [ass.id for ass in task.assignments.list()]
465
-
466
- if filters is None and items is None:
467
- raise exceptions.PlatformException('400', 'Must provide either filters or items list')
468
-
469
- workload = entities.Workload.generate(assignee_ids=[assignee_id])
470
- task = task.add_items(filters=filters, items=items, workload=workload, limit=None)
471
- assignments = [ass for ass in task.assignments.list() if ass.id not in assignments_before]
472
-
473
- if len(assignments) < 1:
474
- raise exceptions.PlatformException('Error creating an assignment, '
475
- 'Please use task.add_items() to perform this action')
476
-
477
- return assignments[0]
478
-
479
- def __item_operations(self, dataset: entities.Dataset,
480
- op, assignment_id=None, assignment_name=None, filters=None, items=None):
481
- if assignment_id is None and assignment_name is None:
482
- raise exceptions.PlatformException('400', 'Must provide either assignment name or assignment id')
483
- elif assignment_id is None:
484
- assignment_id = self.get(assignment_name=assignment_name).id
485
-
486
- try:
487
- if filters is None and items is None:
488
- raise exceptions.PlatformException('400', 'Must provide either filters or items list')
489
-
490
- if filters is None:
491
- if not isinstance(items, list):
492
- items = [items]
493
- filters = entities.Filters(field='id',
494
- values=[item.id for item in items],
495
- operator=entities.FiltersOperations.IN,
496
- use_defaults=False)
497
-
498
- filters._ref_assignment = True
499
- filters._ref_assignment_id = assignment_id
500
- filters._ref_op = op
501
-
502
- return dataset.items.update(filters=filters)
503
- finally:
504
- if filters is not None:
505
- filters._nullify_refs()
506
-
507
- def get_items(self,
508
- assignment: entities.Assignment = None,
509
- assignment_id=None,
510
- assignment_name=None,
511
- dataset=None,
512
- filters=None
513
- ) -> entities.PagedEntities:
514
- """
515
- Get all the items in the assignment.
516
-
517
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
518
-
519
- :param dtlpy.entities.assignment.Assignment assignment: assignment object
520
- :param assignment_id: the Id of the assignment
521
- :param str assignment_name: the name of the assignment
522
- :param dtlpy.entities.dataset.Dataset dataset: dataset object, the dataset that refer to the assignment
523
- :param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
524
- :return: pages of the items
525
- :rtype: dtlpy.entities.paged_entities.PagedEntities
526
-
527
- **Example**:
528
-
529
- .. code-block:: python
530
-
531
- items = task.assignments.get_items(assignment_id='assignment_id')
532
- """
533
- if assignment is None and assignment_id is None and assignment_name is None:
534
- raise exceptions.PlatformException('400',
535
- 'Please provide either assignment, assignment_id or assignment_name')
536
-
537
- if assignment_id is None:
538
- if assignment is None:
539
- assignment = self.get(assignment_name=assignment_name)
540
- assignment_id = assignment.id
541
-
542
- if dataset is None and self._dataset is None:
543
- if assignment is None:
544
- assignment = self.get(assignment_id=assignment_id, assignment_name=assignment_name)
545
- if assignment.dataset_id is None:
546
- raise exceptions.PlatformException('400', 'Please provide a dataset entity')
547
- dataset = repositories.Datasets(client_api=self._client_api, project=self._project).get(
548
- dataset_id=assignment.dataset_id)
549
- elif dataset is None:
550
- dataset = self._dataset
551
-
552
- if filters is None:
553
- filters = entities.Filters(use_defaults=False)
554
- filters.add(field='metadata.system.refs.id', values=[assignment_id], operator=entities.FiltersOperations.IN)
555
-
556
- return dataset.items.list(filters=filters)
557
-
558
- def set_status(self,
559
- status: str,
560
- operation: str,
561
- item_id: str,
562
- assignment_id: str
563
- ) -> bool:
564
- """
565
- Set item status within assignment.
566
-
567
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
568
-
569
- :param str status: string the describes the status
570
- :param str operation: the status action need 'create' or 'delete'
571
- :param str item_id: item id that want to set his status
572
- :param assignment_id: the Id of the assignment
573
- :return: True id success
574
- :rtype: bool
575
-
576
- **Example**:
577
-
578
- .. code-block:: python
579
-
580
- success = task.assignments.set_status(assignment_id='assignment_id',
581
- status='complete',
582
- operation='created',
583
- item_id='item_id')
584
- """
585
- url = '/assignments/{assignment_id}/items/{item_id}/status'.format(assignment_id=assignment_id, item_id=item_id)
586
- payload = {
587
- 'operation': operation,
588
- 'status': status
589
- }
590
- success, response = self._client_api.gen_request(
591
- req_type='post',
592
- path=url,
593
- json_req=payload
594
- )
595
-
596
- if not success:
597
- raise exceptions.PlatformException(response)
598
-
599
- return True
1
+ import logging
2
+
3
+ from .. import exceptions, miscellaneous, entities, repositories, _api_reference
4
+ from ..services.api_client import ApiClient
5
+
6
+ logger = logging.getLogger(name='dtlpy')
7
+
8
+
9
+ class Assignments:
10
+ """
11
+ Assignments Repository
12
+
13
+ The Assignments class allows users to manage assignments and their properties.
14
+ Read more about `Task Assignment <https://developers.dataloop.ai/tutorials/task_workflows/create_a_task/chapter/>`_ in our Developers documentation.
15
+ """
16
+
17
+ def __init__(self,
18
+ client_api: ApiClient,
19
+ project: entities.Project = None,
20
+ task: entities.Task = None,
21
+ dataset: entities.Dataset = None,
22
+ project_id=None):
23
+ self._client_api = client_api
24
+ self._project = project
25
+ self._dataset = dataset
26
+ self._task = task
27
+
28
+ self._project_id = project_id
29
+ if self._project_id is None and self._project is not None:
30
+ self._project_id = self._project.id
31
+
32
+ ############
33
+ # entities #
34
+ ############
35
+ @property
36
+ def task(self) -> entities.Task:
37
+ if self._task is None:
38
+ raise exceptions.PlatformException(
39
+ error='2001',
40
+ message='Missing "task". need to set an Task entity or use task.assignments repository')
41
+ assert isinstance(self._task, entities.Task)
42
+ return self._task
43
+
44
+ @task.setter
45
+ def task(self, task: entities.Task):
46
+ if not isinstance(task, entities.Task):
47
+ raise ValueError('Must input a valid Task entity')
48
+ self._task = task
49
+
50
+ @property
51
+ def project_id(self):
52
+ if self._project_id is not None:
53
+ return self._project_id
54
+ elif self._project is not None:
55
+ return self._project.id
56
+ else:
57
+ return None
58
+
59
+ @property
60
+ def project(self) -> entities.Project:
61
+ if self._project is None:
62
+ raise exceptions.PlatformException(
63
+ error='2001',
64
+ message='Missing "project". need to set a Project entity or use project.assignments repository')
65
+ assert isinstance(self._project, entities.Project)
66
+ return self._project
67
+
68
+ @project.setter
69
+ def project(self, project: entities.Project):
70
+ if not isinstance(project, entities.Project):
71
+ raise ValueError('Must input a valid Project entity')
72
+ self._project = project
73
+
74
+ ###########
75
+ # methods #
76
+ ###########
77
+ def list(self,
78
+ project_ids: list = None,
79
+ status: str = None,
80
+ assignment_name: str = None,
81
+ assignee_id: str = None,
82
+ pages_size: int = None,
83
+ page_offset: int = None,
84
+ task_id: int = None
85
+ ) -> miscellaneous.List[entities.Assignment]:
86
+ """
87
+ Get Assignment list to be able to use it in your code.
88
+
89
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
90
+
91
+ :param list project_ids: search assignment by given list of project ids
92
+ :param str status: search assignment by a given task status
93
+ :param str assignment_name: search assignment by a given assignment name
94
+ :param str assignee_id: the user email that assignee the assignment to it
95
+ :param int pages_size: pages size of the output generator
96
+ :param int page_offset: page offset of the output generator
97
+ :param str task_id: search assignment by given task id
98
+ :return: List of Assignment objects
99
+ :rtype: miscellaneous.List[dtlpy.entities.assignment.Assignment]
100
+
101
+ **Example**:
102
+
103
+ .. code-block:: python
104
+
105
+ assignments = task.assignments.list(status='complete', assignee_id='user@dataloop.ai', pages_size=100, page_offset=0)
106
+ """
107
+
108
+ # url
109
+ url = '/assignments'
110
+
111
+ query = list()
112
+ if project_ids is not None:
113
+ if not isinstance(project_ids, list):
114
+ project_ids = [project_ids]
115
+ elif self._project_id is not None:
116
+ project_ids = [self._project_id]
117
+ elif self._project is not None:
118
+ project_ids = [self._project.id]
119
+ else:
120
+ raise exceptions.PlatformException(error='400', message='Must provide project')
121
+
122
+ project_ids = ','.join(project_ids)
123
+ query.append('projects={}'.format(project_ids))
124
+
125
+ if status is not None:
126
+ query.append('status={}'.format(status))
127
+ if assignment_name is not None:
128
+ query.append('name={}'.format(assignment_name))
129
+ if assignee_id is not None:
130
+ query.append('annotator={}'.format(assignee_id))
131
+ if pages_size is not None:
132
+ query.append('pageSize={}'.format(pages_size))
133
+ if pages_size is None:
134
+ query.append('pageSize={}'.format(500))
135
+ if page_offset is not None:
136
+ query.append('pageOffset={}'.format(page_offset))
137
+
138
+ if task_id is None and self._task is not None:
139
+ task_id = self._task.id
140
+ if task_id is not None:
141
+ query.append('taskId={}'.format(task_id))
142
+
143
+ if len(query) > 0:
144
+ query_string = '&'.join(query)
145
+ url = '{}?{}'.format(url, query_string)
146
+
147
+ success, response = self._client_api.gen_request(req_type='get',
148
+ path=url)
149
+ if success:
150
+ assignments = miscellaneous.List(
151
+ [entities.Assignment.from_json(client_api=self._client_api,
152
+ _json=_json, project=self._project, dataset=self._dataset,
153
+ task=self._task)
154
+ for _json in response.json()['items']])
155
+ else:
156
+ logger.error('Platform error getting assignments')
157
+ raise exceptions.PlatformException(response)
158
+ return assignments
159
+
160
+ @_api_reference.add(path='/assignments/{id}', method='get')
161
+ def get(self,
162
+ assignment_name: str = None,
163
+ assignment_id: str = None):
164
+ """
165
+ Get Assignment object to use it in your code.
166
+
167
+ :param str assignment_name: optional - search by name
168
+ :param str assignment_id: optional - search by id
169
+ :return: Assignment object
170
+ :rtype: dtlpy.entities.assignment.Assignment
171
+
172
+ **Example**:
173
+
174
+ .. code-block:: python
175
+
176
+ assignment = task.assignments.get(assignment_id='assignment_id')
177
+ """
178
+
179
+ if assignment_id is not None:
180
+ url = '/assignments/{}'.format(assignment_id)
181
+ success, response = self._client_api.gen_request(req_type='get',
182
+ path=url)
183
+ if not success:
184
+ raise exceptions.PlatformException('404', 'Assignment not found')
185
+ else:
186
+ assignment = entities.Assignment.from_json(_json=response.json(),
187
+ client_api=self._client_api,
188
+ project=self._project,
189
+ dataset=self._dataset,
190
+ task=self._task)
191
+ # verify input assignment name is same as the given id
192
+ if assignment_name is not None and assignment.name != assignment_name:
193
+ logger.warning(
194
+ "Mismatch found in assignments.get: assignment_name is different then assignment.name: "
195
+ "{!r} != {!r}".format(
196
+ assignment_name,
197
+ assignment.name))
198
+ elif assignment_name is not None:
199
+ assignments = [assignment for assignment in self.list() if assignment.name == assignment_name]
200
+ if len(assignments) == 0:
201
+ raise exceptions.PlatformException('404', 'Assignment not found')
202
+ elif len(assignments) > 1:
203
+ raise exceptions.PlatformException('404',
204
+ 'More than one assignment exist with the same name: {}'.format(
205
+ assignment_name))
206
+ else:
207
+ assignment = assignments[0]
208
+ else:
209
+ raise exceptions.PlatformException('400', 'Must provide either assignment name or assignment id')
210
+
211
+ assert isinstance(assignment, entities.Assignment)
212
+ return assignment
213
+
214
+ @property
215
+ def platform_url(self):
216
+ if self.task.id is None or self.project_id is None:
217
+ raise ValueError("must have project and task")
218
+
219
+ return self._client_api._get_resource_url(
220
+ "projects/{}/tasks/{}/assignments".format(self.project_id, self.task.id))
221
+
222
+ def open_in_web(self,
223
+ assignment_name: str = None,
224
+ assignment_id: str = None,
225
+ assignment: str = None):
226
+ """
227
+ Open the assignment in the platform.
228
+
229
+ **Prerequisites**: All users.
230
+
231
+ :param str assignment_name: the name of the assignment
232
+ :param str assignment_id: the Id of the assignment
233
+ :param dtlpy.entities.assignment.Assignment assignment: assignment object
234
+
235
+ **Example**:
236
+
237
+ .. code-block:: python
238
+
239
+ task.assignments.open_in_web(assignment_id='assignment_id')
240
+ """
241
+ if assignment_name is not None:
242
+ assignment = self.get(assignment_name=assignment_name)
243
+ if assignment is not None:
244
+ assignment.open_in_web()
245
+ elif assignment_id is not None:
246
+ self._client_api._open_in_web(url=self.platform_url + '/' + str(assignment_id))
247
+ else:
248
+ self._client_api._open_in_web(url=self.platform_url)
249
+
250
+ @_api_reference.add(path='/assignments/{id}/reassign', method='post')
251
+ def reassign(self,
252
+ assignee_id: str,
253
+ assignment: entities.Assignment = None,
254
+ assignment_id: str = None,
255
+ task: entities.Task = None,
256
+ task_id: str = None,
257
+ wait: bool = True):
258
+ """
259
+ Reassign an assignment.
260
+
261
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
262
+
263
+ :param str assignee_id: the email of the user that want to assign the assignment
264
+ :param dtlpy.entities.assignment.Assignment assignment: assignment object
265
+ :param assignment_id: the Id of the assignment
266
+ :param dtlpy.entities.task.Task task: task object
267
+ :param str task_id: the Id of the task that include the assignment
268
+ :param bool wait: wait until reassign assignment finish
269
+ :return: Assignment object
270
+ :rtype: dtlpy.entities.assignment.Assignment
271
+
272
+ **Example**:
273
+
274
+ .. code-block:: python
275
+
276
+ assignment = task.assignments.reassign(assignee_ids='annotator1@dataloop.ai')
277
+ """
278
+ if assignment_id is None and assignment is None:
279
+ raise exceptions.PlatformException('400', 'Must provide either assignment or assignment_id')
280
+ elif assignment_id is None:
281
+ assignment_id = assignment.id
282
+
283
+ if task_id is None and task is None:
284
+ raise exceptions.PlatformException('400', 'Must provide either task or task_id')
285
+ elif task_id is None:
286
+ task_id = task.id
287
+
288
+ url = '/assignments/{}/reassign'.format(assignment_id)
289
+
290
+ payload = {
291
+ 'taskId': task_id,
292
+ 'annotator': assignee_id,
293
+ 'asynced': wait
294
+ }
295
+
296
+ success, response = self._client_api.gen_request(req_type='post',
297
+ path=url,
298
+ json_req=payload)
299
+ if success:
300
+ command = entities.Command.from_json(_json=response.json(),
301
+ client_api=self._client_api)
302
+ if not wait:
303
+ return command
304
+ command = command.wait(timeout=0)
305
+ if 'toAssignment' not in command.spec:
306
+ raise exceptions.PlatformException(error='400',
307
+ message="'toAssignment' key is missing in command response: {}"
308
+ .format(response))
309
+ return self.get(assignment_id=command.spec['toAssignment'])
310
+ else:
311
+ raise exceptions.PlatformException(response)
312
+
313
+ @_api_reference.add(path='/assignments/{id}/redistribute', method='post')
314
+ def redistribute(self,
315
+ workload: entities.Workload,
316
+ assignment: entities.Assignment = None,
317
+ assignment_id: str = None,
318
+ task: entities.Task = None,
319
+ task_id: str = None,
320
+ wait: bool = True):
321
+ """
322
+ Redistribute an assignment.
323
+
324
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
325
+
326
+ **Example**:
327
+
328
+ :param dtlpy.entities.assignment.Workload workload: list of WorkloadUnit objects. Customize distribution (percentage) between the task assignees. For example: [dl.WorkloadUnit(annotator@hi.com, 80), dl.WorkloadUnit(annotator2@hi.com, 20)]
329
+ :param dtlpy.entities.assignment.Assignment assignment: assignment object
330
+ :param str assignment_id: the Id of the assignment
331
+ :param dtlpy.entities.task.Task task: the task object that include the assignment
332
+ :param str task_id: the Id of the task that include the assignment
333
+ :param bool wait: wait until redistribute assignment finish
334
+ :return: Assignment object
335
+ :rtype: dtlpy.entities.assignment.Assignment assignment
336
+
337
+ .. code-block:: python
338
+
339
+ assignment = task.assignments.redistribute(workload=dl.Workload([dl.WorkloadUnit(assignee_id="annotator1@dataloop.ai", load=50),
340
+ dl.WorkloadUnit(assignee_id="annotator2@dataloop.ai", load=50)]))
341
+ """
342
+ if assignment_id is None and assignment is None:
343
+ raise exceptions.PlatformException('400', 'Must provide either assignment or assignment_id')
344
+ elif assignment_id is None:
345
+ assignment_id = assignment.id
346
+
347
+ if task_id is None and task is None:
348
+ raise exceptions.PlatformException('400', 'Must provide either task or task_id')
349
+ elif task_id is None:
350
+ task_id = task.id
351
+
352
+ url = '/assignments/{}/redistribute'.format(assignment_id)
353
+
354
+ payload = {
355
+ 'taskId': task_id,
356
+ 'workload': workload.to_json(),
357
+ 'asynced': wait
358
+ }
359
+
360
+ if self._task is None:
361
+ self._task = self.get(assignment_id=assignment_id).task
362
+ if task is None:
363
+ task = self._task
364
+
365
+ success, response = self._client_api.gen_request(req_type='post',
366
+ path=url,
367
+ json_req=payload)
368
+ if success:
369
+ command = entities.Command.from_json(_json=response.json(),
370
+ client_api=self._client_api)
371
+ if not wait:
372
+ return command
373
+ command = command.wait(timeout=0)
374
+ if 'workload' not in command.spec:
375
+ raise exceptions.PlatformException(error='400',
376
+ message="workload key is missing in command response: {}"
377
+ .format(response))
378
+
379
+ task_assignments = task.assignments.list()
380
+ workers = list()
381
+ for worker in workload:
382
+ workers.append(worker.assignee_id.lower())
383
+
384
+ redistributed_assignments = list()
385
+ for ass in task_assignments:
386
+ if ass.annotator in workers:
387
+ redistributed_assignments.append(ass)
388
+ workers.remove(ass.annotator)
389
+ if not workers:
390
+ break
391
+
392
+ return miscellaneous.List(redistributed_assignments)
393
+ else:
394
+ raise exceptions.PlatformException(response)
395
+
396
+ @_api_reference.add(path='/assignments/{id}', method='patch')
397
+ def update(self,
398
+ assignment: entities.Assignment = None,
399
+ system_metadata: bool = False
400
+ ) -> entities.Assignment:
401
+ """
402
+ Update an assignment.
403
+
404
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
405
+
406
+ :param dtlpy.entities.assignment.Assignment assignment assignment: assignment entity
407
+ :param bool system_metadata: True, if you want to change metadata system
408
+ :return: Assignment object
409
+ :rtype: dtlpy.entities.assignment.Assignment assignment
410
+
411
+ **Example**:
412
+
413
+ .. code-block:: python
414
+
415
+ assignment = task.assignments.update(assignment='assignment_entity', system_metadata=False)
416
+ """
417
+ url = '/assignments/{}'.format(assignment.id)
418
+
419
+ if system_metadata:
420
+ url += '?system=true'
421
+
422
+ success, response = self._client_api.gen_request(req_type='patch',
423
+ path=url,
424
+ json_req=assignment.to_json())
425
+ if success:
426
+ return entities.Assignment.from_json(_json=response.json(),
427
+ client_api=self._client_api, project=self._project,
428
+ dataset=self._dataset, task=self._task)
429
+ else:
430
+ raise exceptions.PlatformException(response)
431
+
432
+ def create(self,
433
+ assignee_id: str,
434
+ task: entities.Task = None,
435
+ filters: entities.Filters = None,
436
+ items: list = None) -> entities.Assignment:
437
+ """
438
+ Create a new assignment.
439
+
440
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
441
+
442
+ :param str assignee_id: the email of the user that want to assign the assignment
443
+ :param dtlpy.entities.task.Task task: the task object that include the assignment
444
+ :param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
445
+ :param list items: list of items (item Id or objects) to insert to the assignment
446
+ :return: Assignment object
447
+ :rtype: dtlpy.entities.assignment.Assignment assignment
448
+
449
+ **Example**:
450
+
451
+ .. code-block:: python
452
+
453
+ assignment = task.assignments.create(assignee_id='annotator1@dataloop.ai')
454
+ """
455
+ return self._create_in_task(assignee_id=assignee_id, task=task, filters=filters, items=items)
456
+
457
+ def _create_in_task(self, assignee_id, task, filters=None, items=None) -> entities.Assignment:
458
+
459
+ if task is None:
460
+ if self._task is None:
461
+ raise exceptions.PlatformException('400', 'Must provide task')
462
+ task = self._task
463
+
464
+ assignments_before = [ass.id for ass in task.assignments.list()]
465
+
466
+ if filters is None and items is None:
467
+ raise exceptions.PlatformException('400', 'Must provide either filters or items list')
468
+
469
+ workload = entities.Workload.generate(assignee_ids=[assignee_id])
470
+ task = task.add_items(filters=filters, items=items, workload=workload, limit=None)
471
+ assignments = [ass for ass in task.assignments.list() if ass.id not in assignments_before]
472
+
473
+ if len(assignments) < 1:
474
+ raise exceptions.PlatformException('Error creating an assignment, '
475
+ 'Please use task.add_items() to perform this action')
476
+
477
+ return assignments[0]
478
+
479
+ def __item_operations(self, dataset: entities.Dataset,
480
+ op, assignment_id=None, assignment_name=None, filters=None, items=None):
481
+ if assignment_id is None and assignment_name is None:
482
+ raise exceptions.PlatformException('400', 'Must provide either assignment name or assignment id')
483
+ elif assignment_id is None:
484
+ assignment_id = self.get(assignment_name=assignment_name).id
485
+
486
+ try:
487
+ if filters is None and items is None:
488
+ raise exceptions.PlatformException('400', 'Must provide either filters or items list')
489
+
490
+ if filters is None:
491
+ if not isinstance(items, list):
492
+ items = [items]
493
+ filters = entities.Filters(field='id',
494
+ values=[item.id for item in items],
495
+ operator=entities.FiltersOperations.IN,
496
+ use_defaults=False)
497
+
498
+ filters._ref_assignment = True
499
+ filters._ref_assignment_id = assignment_id
500
+ filters._ref_op = op
501
+
502
+ return dataset.items.update(filters=filters)
503
+ finally:
504
+ if filters is not None:
505
+ filters._nullify_refs()
506
+
507
+ def get_items(self,
508
+ assignment: entities.Assignment = None,
509
+ assignment_id=None,
510
+ assignment_name=None,
511
+ dataset=None,
512
+ filters=None
513
+ ) -> entities.PagedEntities:
514
+ """
515
+ Get all the items in the assignment.
516
+
517
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
518
+
519
+ :param dtlpy.entities.assignment.Assignment assignment: assignment object
520
+ :param assignment_id: the Id of the assignment
521
+ :param str assignment_name: the name of the assignment
522
+ :param dtlpy.entities.dataset.Dataset dataset: dataset object, the dataset that refer to the assignment
523
+ :param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
524
+ :return: pages of the items
525
+ :rtype: dtlpy.entities.paged_entities.PagedEntities
526
+
527
+ **Example**:
528
+
529
+ .. code-block:: python
530
+
531
+ items = task.assignments.get_items(assignment_id='assignment_id')
532
+ """
533
+ if assignment is None and assignment_id is None and assignment_name is None:
534
+ raise exceptions.PlatformException('400',
535
+ 'Please provide either assignment, assignment_id or assignment_name')
536
+
537
+ if assignment_id is None:
538
+ if assignment is None:
539
+ assignment = self.get(assignment_name=assignment_name)
540
+ assignment_id = assignment.id
541
+
542
+ if dataset is None and self._dataset is None:
543
+ if assignment is None:
544
+ assignment = self.get(assignment_id=assignment_id, assignment_name=assignment_name)
545
+ if assignment.dataset_id is None:
546
+ raise exceptions.PlatformException('400', 'Please provide a dataset entity')
547
+ dataset = repositories.Datasets(client_api=self._client_api, project=self._project).get(
548
+ dataset_id=assignment.dataset_id)
549
+ elif dataset is None:
550
+ dataset = self._dataset
551
+
552
+ if filters is None:
553
+ filters = entities.Filters(use_defaults=False)
554
+ filters.add(field='metadata.system.refs.id', values=[assignment_id], operator=entities.FiltersOperations.IN)
555
+
556
+ return dataset.items.list(filters=filters)
557
+
558
+ def set_status(self,
559
+ status: str,
560
+ operation: str,
561
+ item_id: str,
562
+ assignment_id: str
563
+ ) -> bool:
564
+ """
565
+ Set item status within assignment.
566
+
567
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
568
+
569
+ :param str status: string the describes the status
570
+ :param str operation: the status action need 'create' or 'delete'
571
+ :param str item_id: item id that want to set his status
572
+ :param assignment_id: the Id of the assignment
573
+ :return: True id success
574
+ :rtype: bool
575
+
576
+ **Example**:
577
+
578
+ .. code-block:: python
579
+
580
+ success = task.assignments.set_status(assignment_id='assignment_id',
581
+ status='complete',
582
+ operation='created',
583
+ item_id='item_id')
584
+ """
585
+ url = '/assignments/{assignment_id}/items/{item_id}/status'.format(assignment_id=assignment_id, item_id=item_id)
586
+ payload = {
587
+ 'operation': operation,
588
+ 'status': status
589
+ }
590
+ success, response = self._client_api.gen_request(
591
+ req_type='post',
592
+ path=url,
593
+ json_req=payload
594
+ )
595
+
596
+ if not success:
597
+ raise exceptions.PlatformException(response)
598
+
599
+ return True