dtlpy 1.114.17__py3-none-any.whl → 1.116.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. dtlpy/__init__.py +491 -491
  2. dtlpy/__version__.py +1 -1
  3. dtlpy/assets/__init__.py +26 -26
  4. dtlpy/assets/code_server/config.yaml +2 -2
  5. dtlpy/assets/code_server/installation.sh +24 -24
  6. dtlpy/assets/code_server/launch.json +13 -13
  7. dtlpy/assets/code_server/settings.json +2 -2
  8. dtlpy/assets/main.py +53 -53
  9. dtlpy/assets/main_partial.py +18 -18
  10. dtlpy/assets/mock.json +11 -11
  11. dtlpy/assets/model_adapter.py +83 -83
  12. dtlpy/assets/package.json +61 -61
  13. dtlpy/assets/package_catalog.json +29 -29
  14. dtlpy/assets/package_gitignore +307 -307
  15. dtlpy/assets/service_runners/__init__.py +33 -33
  16. dtlpy/assets/service_runners/converter.py +96 -96
  17. dtlpy/assets/service_runners/multi_method.py +49 -49
  18. dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
  19. dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
  20. dtlpy/assets/service_runners/multi_method_item.py +52 -52
  21. dtlpy/assets/service_runners/multi_method_json.py +52 -52
  22. dtlpy/assets/service_runners/single_method.py +37 -37
  23. dtlpy/assets/service_runners/single_method_annotation.py +43 -43
  24. dtlpy/assets/service_runners/single_method_dataset.py +43 -43
  25. dtlpy/assets/service_runners/single_method_item.py +41 -41
  26. dtlpy/assets/service_runners/single_method_json.py +42 -42
  27. dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
  28. dtlpy/assets/voc_annotation_template.xml +23 -23
  29. dtlpy/caches/base_cache.py +32 -32
  30. dtlpy/caches/cache.py +473 -473
  31. dtlpy/caches/dl_cache.py +201 -201
  32. dtlpy/caches/filesystem_cache.py +89 -89
  33. dtlpy/caches/redis_cache.py +84 -84
  34. dtlpy/dlp/__init__.py +20 -20
  35. dtlpy/dlp/cli_utilities.py +367 -367
  36. dtlpy/dlp/command_executor.py +764 -764
  37. dtlpy/dlp/dlp +1 -1
  38. dtlpy/dlp/dlp.bat +1 -1
  39. dtlpy/dlp/dlp.py +128 -128
  40. dtlpy/dlp/parser.py +651 -651
  41. dtlpy/entities/__init__.py +83 -83
  42. dtlpy/entities/analytic.py +347 -311
  43. dtlpy/entities/annotation.py +1879 -1879
  44. dtlpy/entities/annotation_collection.py +699 -699
  45. dtlpy/entities/annotation_definitions/__init__.py +20 -20
  46. dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
  47. dtlpy/entities/annotation_definitions/box.py +195 -195
  48. dtlpy/entities/annotation_definitions/classification.py +67 -67
  49. dtlpy/entities/annotation_definitions/comparison.py +72 -72
  50. dtlpy/entities/annotation_definitions/cube.py +204 -204
  51. dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
  52. dtlpy/entities/annotation_definitions/description.py +32 -32
  53. dtlpy/entities/annotation_definitions/ellipse.py +124 -124
  54. dtlpy/entities/annotation_definitions/free_text.py +62 -62
  55. dtlpy/entities/annotation_definitions/gis.py +69 -69
  56. dtlpy/entities/annotation_definitions/note.py +139 -139
  57. dtlpy/entities/annotation_definitions/point.py +117 -117
  58. dtlpy/entities/annotation_definitions/polygon.py +182 -182
  59. dtlpy/entities/annotation_definitions/polyline.py +111 -111
  60. dtlpy/entities/annotation_definitions/pose.py +92 -92
  61. dtlpy/entities/annotation_definitions/ref_image.py +86 -86
  62. dtlpy/entities/annotation_definitions/segmentation.py +240 -240
  63. dtlpy/entities/annotation_definitions/subtitle.py +34 -34
  64. dtlpy/entities/annotation_definitions/text.py +85 -85
  65. dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
  66. dtlpy/entities/app.py +220 -220
  67. dtlpy/entities/app_module.py +107 -107
  68. dtlpy/entities/artifact.py +174 -174
  69. dtlpy/entities/assignment.py +399 -399
  70. dtlpy/entities/base_entity.py +214 -214
  71. dtlpy/entities/bot.py +113 -113
  72. dtlpy/entities/codebase.py +292 -296
  73. dtlpy/entities/collection.py +38 -38
  74. dtlpy/entities/command.py +169 -169
  75. dtlpy/entities/compute.py +449 -442
  76. dtlpy/entities/dataset.py +1299 -1285
  77. dtlpy/entities/directory_tree.py +44 -44
  78. dtlpy/entities/dpk.py +470 -470
  79. dtlpy/entities/driver.py +235 -223
  80. dtlpy/entities/execution.py +397 -397
  81. dtlpy/entities/feature.py +124 -124
  82. dtlpy/entities/feature_set.py +145 -145
  83. dtlpy/entities/filters.py +798 -645
  84. dtlpy/entities/gis_item.py +107 -107
  85. dtlpy/entities/integration.py +184 -184
  86. dtlpy/entities/item.py +959 -953
  87. dtlpy/entities/label.py +123 -123
  88. dtlpy/entities/links.py +85 -85
  89. dtlpy/entities/message.py +175 -175
  90. dtlpy/entities/model.py +684 -684
  91. dtlpy/entities/node.py +1005 -1005
  92. dtlpy/entities/ontology.py +810 -803
  93. dtlpy/entities/organization.py +287 -287
  94. dtlpy/entities/package.py +657 -657
  95. dtlpy/entities/package_defaults.py +5 -5
  96. dtlpy/entities/package_function.py +185 -185
  97. dtlpy/entities/package_module.py +113 -113
  98. dtlpy/entities/package_slot.py +118 -118
  99. dtlpy/entities/paged_entities.py +299 -299
  100. dtlpy/entities/pipeline.py +624 -624
  101. dtlpy/entities/pipeline_execution.py +279 -279
  102. dtlpy/entities/project.py +394 -394
  103. dtlpy/entities/prompt_item.py +505 -499
  104. dtlpy/entities/recipe.py +301 -301
  105. dtlpy/entities/reflect_dict.py +102 -102
  106. dtlpy/entities/resource_execution.py +138 -138
  107. dtlpy/entities/service.py +963 -958
  108. dtlpy/entities/service_driver.py +117 -117
  109. dtlpy/entities/setting.py +294 -294
  110. dtlpy/entities/task.py +495 -495
  111. dtlpy/entities/time_series.py +143 -143
  112. dtlpy/entities/trigger.py +426 -426
  113. dtlpy/entities/user.py +118 -118
  114. dtlpy/entities/webhook.py +124 -124
  115. dtlpy/examples/__init__.py +19 -19
  116. dtlpy/examples/add_labels.py +135 -135
  117. dtlpy/examples/add_metadata_to_item.py +21 -21
  118. dtlpy/examples/annotate_items_using_model.py +65 -65
  119. dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
  120. dtlpy/examples/annotations_convert_to_voc.py +9 -9
  121. dtlpy/examples/annotations_convert_to_yolo.py +9 -9
  122. dtlpy/examples/convert_annotation_types.py +51 -51
  123. dtlpy/examples/converter.py +143 -143
  124. dtlpy/examples/copy_annotations.py +22 -22
  125. dtlpy/examples/copy_folder.py +31 -31
  126. dtlpy/examples/create_annotations.py +51 -51
  127. dtlpy/examples/create_video_annotations.py +83 -83
  128. dtlpy/examples/delete_annotations.py +26 -26
  129. dtlpy/examples/filters.py +113 -113
  130. dtlpy/examples/move_item.py +23 -23
  131. dtlpy/examples/play_video_annotation.py +13 -13
  132. dtlpy/examples/show_item_and_mask.py +53 -53
  133. dtlpy/examples/triggers.py +49 -49
  134. dtlpy/examples/upload_batch_of_items.py +20 -20
  135. dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
  136. dtlpy/examples/upload_items_with_modalities.py +43 -43
  137. dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
  138. dtlpy/examples/upload_yolo_format_annotations.py +70 -70
  139. dtlpy/exceptions.py +125 -125
  140. dtlpy/miscellaneous/__init__.py +20 -20
  141. dtlpy/miscellaneous/dict_differ.py +95 -95
  142. dtlpy/miscellaneous/git_utils.py +217 -217
  143. dtlpy/miscellaneous/json_utils.py +14 -14
  144. dtlpy/miscellaneous/list_print.py +105 -105
  145. dtlpy/miscellaneous/zipping.py +130 -130
  146. dtlpy/ml/__init__.py +20 -20
  147. dtlpy/ml/base_feature_extractor_adapter.py +27 -27
  148. dtlpy/ml/base_model_adapter.py +1257 -1086
  149. dtlpy/ml/metrics.py +461 -461
  150. dtlpy/ml/predictions_utils.py +274 -274
  151. dtlpy/ml/summary_writer.py +57 -57
  152. dtlpy/ml/train_utils.py +60 -60
  153. dtlpy/new_instance.py +252 -252
  154. dtlpy/repositories/__init__.py +56 -56
  155. dtlpy/repositories/analytics.py +85 -85
  156. dtlpy/repositories/annotations.py +916 -916
  157. dtlpy/repositories/apps.py +383 -383
  158. dtlpy/repositories/artifacts.py +452 -452
  159. dtlpy/repositories/assignments.py +599 -599
  160. dtlpy/repositories/bots.py +213 -213
  161. dtlpy/repositories/codebases.py +559 -559
  162. dtlpy/repositories/collections.py +332 -332
  163. dtlpy/repositories/commands.py +152 -158
  164. dtlpy/repositories/compositions.py +61 -61
  165. dtlpy/repositories/computes.py +439 -435
  166. dtlpy/repositories/datasets.py +1504 -1291
  167. dtlpy/repositories/downloader.py +976 -903
  168. dtlpy/repositories/dpks.py +433 -433
  169. dtlpy/repositories/drivers.py +482 -470
  170. dtlpy/repositories/executions.py +815 -817
  171. dtlpy/repositories/feature_sets.py +226 -226
  172. dtlpy/repositories/features.py +255 -238
  173. dtlpy/repositories/integrations.py +484 -484
  174. dtlpy/repositories/items.py +912 -909
  175. dtlpy/repositories/messages.py +94 -94
  176. dtlpy/repositories/models.py +1000 -988
  177. dtlpy/repositories/nodes.py +80 -80
  178. dtlpy/repositories/ontologies.py +511 -511
  179. dtlpy/repositories/organizations.py +525 -525
  180. dtlpy/repositories/packages.py +1941 -1941
  181. dtlpy/repositories/pipeline_executions.py +451 -451
  182. dtlpy/repositories/pipelines.py +640 -640
  183. dtlpy/repositories/projects.py +539 -539
  184. dtlpy/repositories/recipes.py +419 -399
  185. dtlpy/repositories/resource_executions.py +137 -137
  186. dtlpy/repositories/schema.py +120 -120
  187. dtlpy/repositories/service_drivers.py +213 -213
  188. dtlpy/repositories/services.py +1704 -1704
  189. dtlpy/repositories/settings.py +339 -339
  190. dtlpy/repositories/tasks.py +1477 -1477
  191. dtlpy/repositories/times_series.py +278 -278
  192. dtlpy/repositories/triggers.py +536 -536
  193. dtlpy/repositories/upload_element.py +257 -257
  194. dtlpy/repositories/uploader.py +661 -651
  195. dtlpy/repositories/webhooks.py +249 -249
  196. dtlpy/services/__init__.py +22 -22
  197. dtlpy/services/aihttp_retry.py +131 -131
  198. dtlpy/services/api_client.py +1785 -1782
  199. dtlpy/services/api_reference.py +40 -40
  200. dtlpy/services/async_utils.py +133 -133
  201. dtlpy/services/calls_counter.py +44 -44
  202. dtlpy/services/check_sdk.py +68 -68
  203. dtlpy/services/cookie.py +115 -115
  204. dtlpy/services/create_logger.py +156 -156
  205. dtlpy/services/events.py +84 -84
  206. dtlpy/services/logins.py +235 -235
  207. dtlpy/services/reporter.py +256 -256
  208. dtlpy/services/service_defaults.py +91 -91
  209. dtlpy/utilities/__init__.py +20 -20
  210. dtlpy/utilities/annotations/__init__.py +16 -16
  211. dtlpy/utilities/annotations/annotation_converters.py +269 -269
  212. dtlpy/utilities/base_package_runner.py +285 -264
  213. dtlpy/utilities/converter.py +1650 -1650
  214. dtlpy/utilities/dataset_generators/__init__.py +1 -1
  215. dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
  216. dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
  217. dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
  218. dtlpy/utilities/local_development/__init__.py +1 -1
  219. dtlpy/utilities/local_development/local_session.py +179 -179
  220. dtlpy/utilities/reports/__init__.py +2 -2
  221. dtlpy/utilities/reports/figures.py +343 -343
  222. dtlpy/utilities/reports/report.py +71 -71
  223. dtlpy/utilities/videos/__init__.py +17 -17
  224. dtlpy/utilities/videos/video_player.py +598 -598
  225. dtlpy/utilities/videos/videos.py +470 -470
  226. {dtlpy-1.114.17.data → dtlpy-1.116.6.data}/scripts/dlp +1 -1
  227. dtlpy-1.116.6.data/scripts/dlp.bat +2 -0
  228. {dtlpy-1.114.17.data → dtlpy-1.116.6.data}/scripts/dlp.py +128 -128
  229. {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/METADATA +186 -183
  230. dtlpy-1.116.6.dist-info/RECORD +239 -0
  231. {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/WHEEL +1 -1
  232. {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/licenses/LICENSE +200 -200
  233. tests/features/environment.py +551 -551
  234. dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
  235. dtlpy-1.114.17.data/scripts/dlp.bat +0 -2
  236. dtlpy-1.114.17.dist-info/RECORD +0 -240
  237. {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/entry_points.txt +0 -0
  238. {dtlpy-1.114.17.dist-info → dtlpy-1.116.6.dist-info}/top_level.txt +0 -0
@@ -1,399 +1,399 @@
1
- import attr
2
- import logging
3
- from .. import repositories, exceptions, entities
4
-
5
- logger = logging.getLogger(name='dtlpy')
6
-
7
-
8
- @attr.s
9
- class Assignment(entities.BaseEntity):
10
- """
11
- Assignment object
12
- """
13
-
14
- # platform
15
- name = attr.ib()
16
- annotator = attr.ib()
17
- status = attr.ib()
18
- project_id = attr.ib()
19
- metadata = attr.ib(repr=False)
20
- id = attr.ib()
21
- url = attr.ib(repr=False)
22
- updated_at = attr.ib(repr=False)
23
- updated_by = attr.ib(repr=False)
24
- task_id = attr.ib(repr=False)
25
- dataset_id = attr.ib(repr=False)
26
- annotation_status = attr.ib(repr=False)
27
- item_status = attr.ib(repr=False)
28
- total_items = attr.ib()
29
- for_review = attr.ib()
30
- issues = attr.ib()
31
- # sdk
32
-
33
- _client_api = attr.ib(repr=False)
34
- _task = attr.ib(default=None, repr=False)
35
- _assignments = attr.ib(default=None, repr=False)
36
- _project = attr.ib(default=None, repr=False)
37
- _dataset = attr.ib(default=None, repr=False)
38
- _datasets = attr.ib(default=None, repr=False)
39
-
40
- @classmethod
41
- def from_json(cls, _json, client_api, project=None, task=None, dataset=None):
42
- if project is not None:
43
- if project.id != _json.get('projectId', None):
44
- logger.warning('Assignment has been fetched from a project that is not belong to it')
45
- project = None
46
-
47
- metadata = _json.get('metadata', dict())
48
- dataset_id = metadata.get('datasetId', None)
49
- task_id = metadata.get('taskId', None)
50
- if dataset_id is None:
51
- system_metadata = metadata.get('system', dict())
52
- dataset_id = system_metadata.get('datasetId', None)
53
- if task_id is None:
54
- system_metadata = metadata.get('system', dict())
55
- task_id = system_metadata.get('taskId', None)
56
-
57
- assignment = cls(
58
- name=_json.get('name', None),
59
- annotator=_json.get('annotator', None),
60
- status=_json.get('status', None),
61
- project_id=_json.get('projectId', None),
62
- task_id=task_id,
63
- dataset_id=dataset_id,
64
- metadata=metadata,
65
- url=_json.get('url', None),
66
- id=_json['id'],
67
- updated_by=_json.get('updatedBy', None),
68
- updated_at=_json.get('updatedAt', None),
69
- client_api=client_api,
70
- project=project,
71
- dataset=dataset,
72
- task=task,
73
- annotation_status=_json.get('annotationStatus', None),
74
- item_status=_json.get('itemStatus', None),
75
- total_items=_json.get('totalItems', None),
76
- for_review=_json.get('forReview', None),
77
- issues=_json.get('issues', None)
78
- )
79
-
80
- if dataset is not None:
81
- if assignment.dataset_id != dataset.id:
82
- logger.warning('Assignment has been fetched from a dataset that is not belong to it')
83
- assignment._dataset = None
84
-
85
- if task is not None:
86
- if assignment.task_id != task.id:
87
- logger.warning('Assignment has been fetched from a task that is not belong to it')
88
- assignment._task = None
89
-
90
- return assignment
91
-
92
- @property
93
- def platform_url(self):
94
- return self._client_api._get_resource_url("projects/{}/assignments/{}".format(self.project_id, self.id))
95
-
96
- @property
97
- def assignments(self):
98
- if self._assignments is None:
99
- self._assignments = repositories.Assignments(client_api=self._client_api,
100
- project=self._project,
101
- task=self._task,
102
- dataset=self._dataset)
103
- assert isinstance(self._assignments, repositories.Assignments)
104
- return self._assignments
105
-
106
- @property
107
- def project(self):
108
- if self._project is None:
109
- self._project = repositories.Projects(client_api=self._client_api).get(project_id=self.project_id,
110
- fetch=None)
111
-
112
- assert isinstance(self._project, entities.Project)
113
- return self._project
114
-
115
- @property
116
- def datasets(self):
117
- if self._datasets is None:
118
- self._datasets = repositories.Datasets(client_api=self._client_api, project=self._project)
119
- assert isinstance(self._datasets, repositories.Datasets)
120
- return self._datasets
121
-
122
- @property
123
- def dataset(self):
124
- if self._dataset is None:
125
- self._dataset = self.datasets.get(dataset_id=self.dataset_id)
126
- assert isinstance(self._dataset, entities.Dataset)
127
- return self._dataset
128
-
129
- @property
130
- def task(self):
131
- if self._task is None:
132
- self._task = self.project.tasks.get(task_id=self.task_id)
133
- assert isinstance(self._task, entities.Task)
134
- return self._task
135
-
136
- def to_json(self):
137
- """
138
- Returns platform _json format of object
139
-
140
- :return: platform json format of object
141
- :rtype: dict
142
- """
143
- _json = attr.asdict(self, filter=attr.filters.exclude(attr.fields(Assignment)._client_api,
144
- attr.fields(Assignment)._project,
145
- attr.fields(Assignment).project_id,
146
- attr.fields(Assignment)._assignments,
147
- attr.fields(Assignment)._dataset,
148
- attr.fields(Assignment)._task,
149
- attr.fields(Assignment).annotation_status,
150
- attr.fields(Assignment).updated_at,
151
- attr.fields(Assignment).updated_by,
152
- attr.fields(Assignment).item_status,
153
- attr.fields(Assignment).total_items,
154
- attr.fields(Assignment).for_review,
155
- attr.fields(Assignment).issues))
156
-
157
- _json['projectId'] = self.project_id
158
- return _json
159
-
160
- def update(self, system_metadata=False):
161
- """
162
- Update an assignment
163
-
164
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
165
-
166
- :param bool system_metadata: True, if you want to change metadata system
167
- :return: Assignment object
168
- :rtype: dtlpy.entities.assignment.Assignment assignment
169
-
170
- **Example**:
171
-
172
- .. code-block:: python
173
-
174
- assignment = assignment.update(system_metadata=False)
175
- """
176
- return self.assignments.update(assignment=self, system_metadata=system_metadata)
177
-
178
- def open_in_web(self):
179
- """
180
- Open the assignment in web platform
181
-
182
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
183
-
184
- :return:
185
-
186
- **Example**:
187
-
188
- .. code-block:: python
189
-
190
- assignment.open_in_web()
191
- """
192
- self._client_api._open_in_web(url=self.platform_url)
193
-
194
- def get_items(self, dataset=None, filters=None):
195
- """
196
- Get all the items in the assignment
197
-
198
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
199
-
200
- :param dtlpy.entities.dataset.Dataset dataset: dataset object, the dataset that refer to the assignment
201
- :param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
202
- :return: pages of the items
203
- :rtype: dtlpy.entities.paged_entities.PagedEntities
204
-
205
- **Example**:
206
-
207
- .. code-block:: python
208
-
209
- items = task.assignments.get_items()
210
- """
211
- if dataset is None:
212
- dataset = self.dataset
213
-
214
- return self.assignments.get_items(dataset=dataset, assignment=self, filters=filters)
215
-
216
- def reassign(self, assignee_id, wait=True):
217
- """
218
- Reassign an assignment
219
-
220
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
221
-
222
- :param str assignee_id: the email of the user that want to assign the assignment
223
- :param bool wait: wait until reassign assignment finish
224
- :return: Assignment object
225
- :rtype: dtlpy.entities.assignment.Assignment
226
-
227
- **Example**:
228
-
229
- .. code-block:: python
230
-
231
- assignment = assignment.reassign(assignee_ids='annotator1@dataloop.ai')
232
- """
233
- return self.assignments.reassign(assignment=self,
234
- task=self._task,
235
- task_id=self.metadata['system'].get('taskId'),
236
- assignee_id=assignee_id,
237
- wait=wait)
238
-
239
- def redistribute(self, workload, wait=True):
240
- """
241
- Redistribute an assignment
242
-
243
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
244
-
245
- :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)]
246
- :param bool wait: wait until redistribute assignment finish
247
- :return: Assignment object
248
- :rtype: dtlpy.entities.assignment.Assignment assignment
249
-
250
- **Example**:
251
-
252
- .. code-block:: python
253
-
254
- assignment = assignment.redistribute(workload=dl.Workload([dl.WorkloadUnit(assignee_id="annotator1@dataloop.ai", load=50),
255
- dl.WorkloadUnit(assignee_id="annotator2@dataloop.ai", load=50)]))
256
- """
257
- return self.assignments.redistribute(assignment=self,
258
- task=self._task,
259
- task_id=self.metadata['system'].get('taskId'),
260
- workload=workload,
261
- wait=wait)
262
-
263
- def set_status(self, status: str, operation: str, item_id: str):
264
- """
265
- Set item status within assignment
266
-
267
- **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
268
-
269
- :param str status: string the describes the status
270
- :param str operation: the status action need 'create' or 'delete'
271
- :param str item_id: item id that want to set his status
272
- :return: True id success
273
- :rtype: bool
274
-
275
- **Example**:
276
-
277
- .. code-block:: python
278
-
279
- success = assignment.set_status(status='complete',
280
- operation='created',
281
- item_id='item_id')
282
- """
283
- return self.assignments.set_status(status=status, operation=operation, item_id=item_id, assignment_id=self.id)
284
-
285
-
286
- @attr.s
287
- class WorkloadUnit:
288
- """
289
- WorkloadUnit object
290
- """
291
-
292
- # platform
293
- assignee_id = attr.ib(type=str)
294
- load = attr.ib(type=float, default=0)
295
-
296
- # noinspection PyUnusedLocal
297
- @load.validator
298
- def check_load(self, attribute, value):
299
- if value < 0 or value > 100:
300
- raise exceptions.PlatformException('400', 'Value must be a number between 0 to 100')
301
-
302
- @classmethod
303
- def from_json(cls, _json):
304
- return cls(
305
- assignee_id=_json.get('assigneeId', None),
306
- load=_json.get('load', None)
307
- )
308
-
309
- def to_json(self):
310
- return {
311
- 'assigneeId': self.assignee_id,
312
- 'load': self.load
313
- }
314
-
315
-
316
- @attr.s
317
- class Workload:
318
- """
319
- Workload object
320
- """
321
-
322
- # platform
323
- workload = attr.ib(type=list)
324
-
325
- def __iter__(self):
326
- for w_l_u in self.workload:
327
- yield w_l_u
328
-
329
- @workload.default
330
- def set_workload(self):
331
- workload = list()
332
- return workload
333
-
334
- @classmethod
335
- def from_json(cls, _json):
336
- return cls(
337
- workload=[WorkloadUnit.from_json(workload_unit) for workload_unit in _json]
338
- )
339
-
340
- @staticmethod
341
- def _loads_are_correct(loads):
342
- return round(sum(loads)) == 100
343
-
344
- @staticmethod
345
- def _get_loads(num_assignees):
346
- loads = [0 for _ in range(num_assignees)]
347
- index = 0
348
- for i in range(10000):
349
- loads[index] += 1
350
- if index < num_assignees - 1:
351
- index += 1
352
- else:
353
- index = 0
354
- loads = [l / 100 for l in loads]
355
- return loads
356
-
357
- def _redistribute(self):
358
- load = self._get_loads(num_assignees=len(self.workload))
359
- for i_w_l, w_l in self.workload:
360
- w_l.load = load
361
-
362
- @classmethod
363
- def generate(cls, assignee_ids, loads=None):
364
- """
365
- generate the loads for the given assignee
366
- :param assignee_ids:
367
- :param loads:
368
- """
369
- if not isinstance(assignee_ids, list):
370
- assignee_ids = [assignee_ids]
371
-
372
- if loads is None:
373
- div = len(assignee_ids)
374
- loads = [100 // div + (1 if x < 100 % div else 0) for x in range(div)]
375
- else:
376
- if not isinstance(loads, list):
377
- loads = [loads]
378
- if not Workload._loads_are_correct(loads=loads):
379
- raise exceptions.PlatformException('400', 'Loads must summarized to 100')
380
-
381
- if len(assignee_ids) != len(loads):
382
- raise exceptions.PlatformException('400', 'Assignee ids and loads must be of same length')
383
-
384
- return cls(
385
- workload=[WorkloadUnit(assignee_id=assignee_id, load=loads[i_assignee_id]) for i_assignee_id, assignee_id in
386
- enumerate(assignee_ids)])
387
-
388
- def to_json(self):
389
- return [workload_unit.to_json() for workload_unit in self.workload]
390
-
391
- def add(self, assignee_id):
392
- """
393
- add a assignee
394
-
395
- :param assignee_id:
396
- """
397
- self.workload.append(WorkloadUnit(assignee_id=assignee_id))
398
- if not self._loads_are_correct(loads=[w_l.load for w_l in self.workload]):
399
- self._redistribute()
1
+ import attr
2
+ import logging
3
+ from .. import repositories, exceptions, entities
4
+
5
+ logger = logging.getLogger(name='dtlpy')
6
+
7
+
8
+ @attr.s
9
+ class Assignment(entities.BaseEntity):
10
+ """
11
+ Assignment object
12
+ """
13
+
14
+ # platform
15
+ name = attr.ib()
16
+ annotator = attr.ib()
17
+ status = attr.ib()
18
+ project_id = attr.ib()
19
+ metadata = attr.ib(repr=False)
20
+ id = attr.ib()
21
+ url = attr.ib(repr=False)
22
+ updated_at = attr.ib(repr=False)
23
+ updated_by = attr.ib(repr=False)
24
+ task_id = attr.ib(repr=False)
25
+ dataset_id = attr.ib(repr=False)
26
+ annotation_status = attr.ib(repr=False)
27
+ item_status = attr.ib(repr=False)
28
+ total_items = attr.ib()
29
+ for_review = attr.ib()
30
+ issues = attr.ib()
31
+ # sdk
32
+
33
+ _client_api = attr.ib(repr=False)
34
+ _task = attr.ib(default=None, repr=False)
35
+ _assignments = attr.ib(default=None, repr=False)
36
+ _project = attr.ib(default=None, repr=False)
37
+ _dataset = attr.ib(default=None, repr=False)
38
+ _datasets = attr.ib(default=None, repr=False)
39
+
40
+ @classmethod
41
+ def from_json(cls, _json, client_api, project=None, task=None, dataset=None):
42
+ if project is not None:
43
+ if project.id != _json.get('projectId', None):
44
+ logger.warning('Assignment has been fetched from a project that is not belong to it')
45
+ project = None
46
+
47
+ metadata = _json.get('metadata', dict())
48
+ dataset_id = metadata.get('datasetId', None)
49
+ task_id = metadata.get('taskId', None)
50
+ if dataset_id is None:
51
+ system_metadata = metadata.get('system', dict())
52
+ dataset_id = system_metadata.get('datasetId', None)
53
+ if task_id is None:
54
+ system_metadata = metadata.get('system', dict())
55
+ task_id = system_metadata.get('taskId', None)
56
+
57
+ assignment = cls(
58
+ name=_json.get('name', None),
59
+ annotator=_json.get('annotator', None),
60
+ status=_json.get('status', None),
61
+ project_id=_json.get('projectId', None),
62
+ task_id=task_id,
63
+ dataset_id=dataset_id,
64
+ metadata=metadata,
65
+ url=_json.get('url', None),
66
+ id=_json['id'],
67
+ updated_by=_json.get('updatedBy', None),
68
+ updated_at=_json.get('updatedAt', None),
69
+ client_api=client_api,
70
+ project=project,
71
+ dataset=dataset,
72
+ task=task,
73
+ annotation_status=_json.get('annotationStatus', None),
74
+ item_status=_json.get('itemStatus', None),
75
+ total_items=_json.get('totalItems', None),
76
+ for_review=_json.get('forReview', None),
77
+ issues=_json.get('issues', None)
78
+ )
79
+
80
+ if dataset is not None:
81
+ if assignment.dataset_id != dataset.id:
82
+ logger.warning('Assignment has been fetched from a dataset that is not belong to it')
83
+ assignment._dataset = None
84
+
85
+ if task is not None:
86
+ if assignment.task_id != task.id:
87
+ logger.warning('Assignment has been fetched from a task that is not belong to it')
88
+ assignment._task = None
89
+
90
+ return assignment
91
+
92
+ @property
93
+ def platform_url(self):
94
+ return self._client_api._get_resource_url("projects/{}/assignments/{}".format(self.project_id, self.id))
95
+
96
+ @property
97
+ def assignments(self):
98
+ if self._assignments is None:
99
+ self._assignments = repositories.Assignments(client_api=self._client_api,
100
+ project=self._project,
101
+ task=self._task,
102
+ dataset=self._dataset)
103
+ assert isinstance(self._assignments, repositories.Assignments)
104
+ return self._assignments
105
+
106
+ @property
107
+ def project(self):
108
+ if self._project is None:
109
+ self._project = repositories.Projects(client_api=self._client_api).get(project_id=self.project_id,
110
+ fetch=None)
111
+
112
+ assert isinstance(self._project, entities.Project)
113
+ return self._project
114
+
115
+ @property
116
+ def datasets(self):
117
+ if self._datasets is None:
118
+ self._datasets = repositories.Datasets(client_api=self._client_api, project=self._project)
119
+ assert isinstance(self._datasets, repositories.Datasets)
120
+ return self._datasets
121
+
122
+ @property
123
+ def dataset(self):
124
+ if self._dataset is None:
125
+ self._dataset = self.datasets.get(dataset_id=self.dataset_id)
126
+ assert isinstance(self._dataset, entities.Dataset)
127
+ return self._dataset
128
+
129
+ @property
130
+ def task(self):
131
+ if self._task is None:
132
+ self._task = self.project.tasks.get(task_id=self.task_id)
133
+ assert isinstance(self._task, entities.Task)
134
+ return self._task
135
+
136
+ def to_json(self):
137
+ """
138
+ Returns platform _json format of object
139
+
140
+ :return: platform json format of object
141
+ :rtype: dict
142
+ """
143
+ _json = attr.asdict(self, filter=attr.filters.exclude(attr.fields(Assignment)._client_api,
144
+ attr.fields(Assignment)._project,
145
+ attr.fields(Assignment).project_id,
146
+ attr.fields(Assignment)._assignments,
147
+ attr.fields(Assignment)._dataset,
148
+ attr.fields(Assignment)._task,
149
+ attr.fields(Assignment).annotation_status,
150
+ attr.fields(Assignment).updated_at,
151
+ attr.fields(Assignment).updated_by,
152
+ attr.fields(Assignment).item_status,
153
+ attr.fields(Assignment).total_items,
154
+ attr.fields(Assignment).for_review,
155
+ attr.fields(Assignment).issues))
156
+
157
+ _json['projectId'] = self.project_id
158
+ return _json
159
+
160
+ def update(self, system_metadata=False):
161
+ """
162
+ Update an assignment
163
+
164
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
165
+
166
+ :param bool system_metadata: True, if you want to change metadata system
167
+ :return: Assignment object
168
+ :rtype: dtlpy.entities.assignment.Assignment assignment
169
+
170
+ **Example**:
171
+
172
+ .. code-block:: python
173
+
174
+ assignment = assignment.update(system_metadata=False)
175
+ """
176
+ return self.assignments.update(assignment=self, system_metadata=system_metadata)
177
+
178
+ def open_in_web(self):
179
+ """
180
+ Open the assignment in web platform
181
+
182
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
183
+
184
+ :return:
185
+
186
+ **Example**:
187
+
188
+ .. code-block:: python
189
+
190
+ assignment.open_in_web()
191
+ """
192
+ self._client_api._open_in_web(url=self.platform_url)
193
+
194
+ def get_items(self, dataset=None, filters=None):
195
+ """
196
+ Get all the items in the assignment
197
+
198
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
199
+
200
+ :param dtlpy.entities.dataset.Dataset dataset: dataset object, the dataset that refer to the assignment
201
+ :param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
202
+ :return: pages of the items
203
+ :rtype: dtlpy.entities.paged_entities.PagedEntities
204
+
205
+ **Example**:
206
+
207
+ .. code-block:: python
208
+
209
+ items = task.assignments.get_items()
210
+ """
211
+ if dataset is None:
212
+ dataset = self.dataset
213
+
214
+ return self.assignments.get_items(dataset=dataset, assignment=self, filters=filters)
215
+
216
+ def reassign(self, assignee_id, wait=True):
217
+ """
218
+ Reassign an assignment
219
+
220
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
221
+
222
+ :param str assignee_id: the email of the user that want to assign the assignment
223
+ :param bool wait: wait until reassign assignment finish
224
+ :return: Assignment object
225
+ :rtype: dtlpy.entities.assignment.Assignment
226
+
227
+ **Example**:
228
+
229
+ .. code-block:: python
230
+
231
+ assignment = assignment.reassign(assignee_ids='annotator1@dataloop.ai')
232
+ """
233
+ return self.assignments.reassign(assignment=self,
234
+ task=self._task,
235
+ task_id=self.metadata['system'].get('taskId'),
236
+ assignee_id=assignee_id,
237
+ wait=wait)
238
+
239
+ def redistribute(self, workload, wait=True):
240
+ """
241
+ Redistribute an assignment
242
+
243
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
244
+
245
+ :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)]
246
+ :param bool wait: wait until redistribute assignment finish
247
+ :return: Assignment object
248
+ :rtype: dtlpy.entities.assignment.Assignment assignment
249
+
250
+ **Example**:
251
+
252
+ .. code-block:: python
253
+
254
+ assignment = assignment.redistribute(workload=dl.Workload([dl.WorkloadUnit(assignee_id="annotator1@dataloop.ai", load=50),
255
+ dl.WorkloadUnit(assignee_id="annotator2@dataloop.ai", load=50)]))
256
+ """
257
+ return self.assignments.redistribute(assignment=self,
258
+ task=self._task,
259
+ task_id=self.metadata['system'].get('taskId'),
260
+ workload=workload,
261
+ wait=wait)
262
+
263
+ def set_status(self, status: str, operation: str, item_id: str):
264
+ """
265
+ Set item status within assignment
266
+
267
+ **Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned as *owner* of the annotation task.
268
+
269
+ :param str status: string the describes the status
270
+ :param str operation: the status action need 'create' or 'delete'
271
+ :param str item_id: item id that want to set his status
272
+ :return: True id success
273
+ :rtype: bool
274
+
275
+ **Example**:
276
+
277
+ .. code-block:: python
278
+
279
+ success = assignment.set_status(status='complete',
280
+ operation='created',
281
+ item_id='item_id')
282
+ """
283
+ return self.assignments.set_status(status=status, operation=operation, item_id=item_id, assignment_id=self.id)
284
+
285
+
286
+ @attr.s
287
+ class WorkloadUnit:
288
+ """
289
+ WorkloadUnit object
290
+ """
291
+
292
+ # platform
293
+ assignee_id = attr.ib(type=str)
294
+ load = attr.ib(type=float, default=0)
295
+
296
+ # noinspection PyUnusedLocal
297
+ @load.validator
298
+ def check_load(self, attribute, value):
299
+ if value < 0 or value > 100:
300
+ raise exceptions.PlatformException('400', 'Value must be a number between 0 to 100')
301
+
302
+ @classmethod
303
+ def from_json(cls, _json):
304
+ return cls(
305
+ assignee_id=_json.get('assigneeId', None),
306
+ load=_json.get('load', None)
307
+ )
308
+
309
+ def to_json(self):
310
+ return {
311
+ 'assigneeId': self.assignee_id,
312
+ 'load': self.load
313
+ }
314
+
315
+
316
+ @attr.s
317
+ class Workload:
318
+ """
319
+ Workload object
320
+ """
321
+
322
+ # platform
323
+ workload = attr.ib(type=list)
324
+
325
+ def __iter__(self):
326
+ for w_l_u in self.workload:
327
+ yield w_l_u
328
+
329
+ @workload.default
330
+ def set_workload(self):
331
+ workload = list()
332
+ return workload
333
+
334
+ @classmethod
335
+ def from_json(cls, _json):
336
+ return cls(
337
+ workload=[WorkloadUnit.from_json(workload_unit) for workload_unit in _json]
338
+ )
339
+
340
+ @staticmethod
341
+ def _loads_are_correct(loads):
342
+ return round(sum(loads)) == 100
343
+
344
+ @staticmethod
345
+ def _get_loads(num_assignees):
346
+ loads = [0 for _ in range(num_assignees)]
347
+ index = 0
348
+ for i in range(10000):
349
+ loads[index] += 1
350
+ if index < num_assignees - 1:
351
+ index += 1
352
+ else:
353
+ index = 0
354
+ loads = [l / 100 for l in loads]
355
+ return loads
356
+
357
+ def _redistribute(self):
358
+ load = self._get_loads(num_assignees=len(self.workload))
359
+ for i_w_l, w_l in self.workload:
360
+ w_l.load = load
361
+
362
+ @classmethod
363
+ def generate(cls, assignee_ids, loads=None):
364
+ """
365
+ generate the loads for the given assignee
366
+ :param assignee_ids:
367
+ :param loads:
368
+ """
369
+ if not isinstance(assignee_ids, list):
370
+ assignee_ids = [assignee_ids]
371
+
372
+ if loads is None:
373
+ div = len(assignee_ids)
374
+ loads = [100 // div + (1 if x < 100 % div else 0) for x in range(div)]
375
+ else:
376
+ if not isinstance(loads, list):
377
+ loads = [loads]
378
+ if not Workload._loads_are_correct(loads=loads):
379
+ raise exceptions.PlatformException('400', 'Loads must summarized to 100')
380
+
381
+ if len(assignee_ids) != len(loads):
382
+ raise exceptions.PlatformException('400', 'Assignee ids and loads must be of same length')
383
+
384
+ return cls(
385
+ workload=[WorkloadUnit(assignee_id=assignee_id, load=loads[i_assignee_id]) for i_assignee_id, assignee_id in
386
+ enumerate(assignee_ids)])
387
+
388
+ def to_json(self):
389
+ return [workload_unit.to_json() for workload_unit in self.workload]
390
+
391
+ def add(self, assignee_id):
392
+ """
393
+ add a assignee
394
+
395
+ :param assignee_id:
396
+ """
397
+ self.workload.append(WorkloadUnit(assignee_id=assignee_id))
398
+ if not self._loads_are_correct(loads=[w_l.load for w_l in self.workload]):
399
+ self._redistribute()