dtlpy 1.115.44__py3-none-any.whl → 1.117.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 -347
  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 -292
  73. dtlpy/entities/collection.py +38 -38
  74. dtlpy/entities/command.py +169 -169
  75. dtlpy/entities/compute.py +449 -449
  76. dtlpy/entities/dataset.py +1299 -1299
  77. dtlpy/entities/directory_tree.py +44 -44
  78. dtlpy/entities/dpk.py +470 -470
  79. dtlpy/entities/driver.py +235 -235
  80. dtlpy/entities/execution.py +397 -397
  81. dtlpy/entities/feature.py +124 -124
  82. dtlpy/entities/feature_set.py +152 -145
  83. dtlpy/entities/filters.py +798 -798
  84. dtlpy/entities/gis_item.py +107 -107
  85. dtlpy/entities/integration.py +184 -184
  86. dtlpy/entities/item.py +975 -959
  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 -505
  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 +974 -963
  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 +1287 -1230
  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 -152
  164. dtlpy/repositories/compositions.py +61 -61
  165. dtlpy/repositories/computes.py +439 -439
  166. dtlpy/repositories/datasets.py +1585 -1504
  167. dtlpy/repositories/downloader.py +1157 -923
  168. dtlpy/repositories/dpks.py +433 -433
  169. dtlpy/repositories/drivers.py +482 -482
  170. dtlpy/repositories/executions.py +815 -815
  171. dtlpy/repositories/feature_sets.py +256 -226
  172. dtlpy/repositories/features.py +255 -255
  173. dtlpy/repositories/integrations.py +484 -484
  174. dtlpy/repositories/items.py +912 -912
  175. dtlpy/repositories/messages.py +94 -94
  176. dtlpy/repositories/models.py +1000 -1000
  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 +429 -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 -661
  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 +1786 -1785
  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.115.44.data → dtlpy-1.117.6.data}/scripts/dlp +1 -1
  227. dtlpy-1.117.6.data/scripts/dlp.bat +2 -0
  228. {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp.py +128 -128
  229. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/METADATA +186 -186
  230. dtlpy-1.117.6.dist-info/RECORD +239 -0
  231. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/WHEEL +1 -1
  232. {dtlpy-1.115.44.dist-info → dtlpy-1.117.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.115.44.data/scripts/dlp.bat +0 -2
  236. dtlpy-1.115.44.dist-info/RECORD +0 -240
  237. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/entry_points.txt +0 -0
  238. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.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