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.
- dtlpy/__init__.py +491 -491
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +347 -347
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +292 -292
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -449
- dtlpy/entities/dataset.py +1299 -1299
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -235
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +152 -145
- dtlpy/entities/filters.py +798 -798
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +975 -959
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +684 -684
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +810 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +299 -299
- dtlpy/entities/pipeline.py +624 -624
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +505 -505
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +974 -963
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +495 -495
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +1287 -1230
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -332
- dtlpy/repositories/commands.py +152 -152
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -439
- dtlpy/repositories/datasets.py +1585 -1504
- dtlpy/repositories/downloader.py +1157 -923
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -482
- dtlpy/repositories/executions.py +815 -815
- dtlpy/repositories/feature_sets.py +256 -226
- dtlpy/repositories/features.py +255 -255
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -912
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -1000
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +451 -451
- dtlpy/repositories/pipelines.py +640 -640
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +429 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1477 -1477
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +661 -661
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1786 -1785
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +285 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp +1 -1
- dtlpy-1.117.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/METADATA +186 -186
- dtlpy-1.117.6.dist-info/RECORD +239 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
- dtlpy-1.115.44.dist-info/RECORD +0 -240
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/top_level.txt +0 -0
dtlpy/entities/task.py
CHANGED
|
@@ -1,495 +1,495 @@
|
|
|
1
|
-
import traceback
|
|
2
|
-
from enum import Enum
|
|
3
|
-
from typing import Union, List
|
|
4
|
-
import attr
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
from .. import repositories, entities, exceptions
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(name='dtlpy')
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class AllocationMethod(str, Enum):
|
|
13
|
-
DISTRIBUTION = 'distribution'
|
|
14
|
-
PULLING = 'pulling'
|
|
15
|
-
|
|
16
|
-
class ConsensusTaskType(str, Enum):
|
|
17
|
-
CONSENSUS = 'consensus'
|
|
18
|
-
QUALIFICATION = 'qualification'
|
|
19
|
-
HONEYPOT = 'honeypot'
|
|
20
|
-
|
|
21
|
-
class TaskPriority(int, Enum):
|
|
22
|
-
LOW = 1
|
|
23
|
-
MEDIUM = 2
|
|
24
|
-
HIGH = 3
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class ItemAction:
|
|
28
|
-
def __init__(self, action, display_name=None, color='#FFFFFF', icon=None):
|
|
29
|
-
if not action or not isinstance(action, str):
|
|
30
|
-
raise ValueError('action should be a non-empty string')
|
|
31
|
-
self.action = action
|
|
32
|
-
if not display_name:
|
|
33
|
-
display_name = action
|
|
34
|
-
self.display_name = display_name
|
|
35
|
-
self.color = color
|
|
36
|
-
self.icon = icon
|
|
37
|
-
|
|
38
|
-
@classmethod
|
|
39
|
-
def from_json(cls, _json: dict):
|
|
40
|
-
kwarg = {
|
|
41
|
-
'action': _json.get('action')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if _json.get('displayName', False):
|
|
45
|
-
kwarg['display_name'] = _json['displayName']
|
|
46
|
-
|
|
47
|
-
if _json.get('color', False):
|
|
48
|
-
kwarg['color'] = _json['color']
|
|
49
|
-
|
|
50
|
-
if _json.get('icon', False):
|
|
51
|
-
kwarg['icon'] = _json['icon']
|
|
52
|
-
|
|
53
|
-
return cls(**kwarg)
|
|
54
|
-
|
|
55
|
-
def to_json(self) -> dict:
|
|
56
|
-
_json = {
|
|
57
|
-
'action': self.action,
|
|
58
|
-
'color': self.color,
|
|
59
|
-
'displayName': self.display_name if self.display_name is not None else self.action
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if self.icon is not None:
|
|
63
|
-
_json['icon'] = self.icon
|
|
64
|
-
|
|
65
|
-
return _json
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@attr.s
|
|
69
|
-
class Task:
|
|
70
|
-
"""
|
|
71
|
-
Task object
|
|
72
|
-
"""
|
|
73
|
-
|
|
74
|
-
# platform
|
|
75
|
-
name = attr.ib()
|
|
76
|
-
status = attr.ib()
|
|
77
|
-
project_id = attr.ib()
|
|
78
|
-
metadata = attr.ib(repr=False)
|
|
79
|
-
id = attr.ib()
|
|
80
|
-
url = attr.ib(repr=False)
|
|
81
|
-
task_owner = attr.ib(repr=False)
|
|
82
|
-
item_status = attr.ib(repr=False)
|
|
83
|
-
creator = attr.ib()
|
|
84
|
-
due_date = attr.ib()
|
|
85
|
-
dataset_id = attr.ib()
|
|
86
|
-
spec = attr.ib()
|
|
87
|
-
recipe_id = attr.ib(repr=False)
|
|
88
|
-
query = attr.ib(repr=False)
|
|
89
|
-
assignmentIds = attr.ib(repr=False)
|
|
90
|
-
annotation_status = attr.ib(repr=False)
|
|
91
|
-
progress = attr.ib()
|
|
92
|
-
for_review = attr.ib()
|
|
93
|
-
issues = attr.ib()
|
|
94
|
-
updated_at = attr.ib()
|
|
95
|
-
created_at = attr.ib()
|
|
96
|
-
available_actions = attr.ib()
|
|
97
|
-
total_items = attr.ib()
|
|
98
|
-
priority = attr.ib()
|
|
99
|
-
_description = attr.ib()
|
|
100
|
-
|
|
101
|
-
# sdk
|
|
102
|
-
_client_api = attr.ib(repr=False)
|
|
103
|
-
_current_assignments = attr.ib(default=None, repr=False)
|
|
104
|
-
_assignments = attr.ib(default=None, repr=False)
|
|
105
|
-
_project = attr.ib(default=None, repr=False)
|
|
106
|
-
_dataset = attr.ib(default=None, repr=False)
|
|
107
|
-
_tasks = attr.ib(default=None, repr=False)
|
|
108
|
-
_settings = attr.ib(default=None, repr=False)
|
|
109
|
-
|
|
110
|
-
@property
|
|
111
|
-
def description(self):
|
|
112
|
-
return self._description
|
|
113
|
-
|
|
114
|
-
@description.setter
|
|
115
|
-
def description(self, description):
|
|
116
|
-
if not isinstance(description, str):
|
|
117
|
-
raise ValueError('description should be a string')
|
|
118
|
-
if self._description is None:
|
|
119
|
-
self._description = {}
|
|
120
|
-
self._description['content'] = description
|
|
121
|
-
|
|
122
|
-
@staticmethod
|
|
123
|
-
def _protected_from_json(_json, client_api, project=None, dataset=None):
|
|
124
|
-
"""
|
|
125
|
-
Same as from_json but with try-except to catch if error
|
|
126
|
-
|
|
127
|
-
:param dict _json: platform json that describe the task
|
|
128
|
-
:param client_api: ApiClient object
|
|
129
|
-
:param dtlpy.entities.project.Project project: project object where task will create
|
|
130
|
-
:param dtlpy.entities.dataset.Dataset dataset: dataset object that refer to the task
|
|
131
|
-
:return:
|
|
132
|
-
"""
|
|
133
|
-
try:
|
|
134
|
-
task = Task.from_json(
|
|
135
|
-
_json=_json,
|
|
136
|
-
client_api=client_api,
|
|
137
|
-
project=project,
|
|
138
|
-
dataset=dataset
|
|
139
|
-
)
|
|
140
|
-
status = True
|
|
141
|
-
except Exception:
|
|
142
|
-
task = traceback.format_exc()
|
|
143
|
-
status = False
|
|
144
|
-
return status, task
|
|
145
|
-
|
|
146
|
-
@classmethod
|
|
147
|
-
def from_json(cls, _json, client_api, project=None, dataset=None):
|
|
148
|
-
"""
|
|
149
|
-
Return the task object form the json
|
|
150
|
-
|
|
151
|
-
:param dict _json: platform json that describe the task
|
|
152
|
-
:param client_api: ApiClient object
|
|
153
|
-
:param dtlpy.entities.project.Project project: project object where task will create
|
|
154
|
-
:param dtlpy.entities.dataset.Dataset dataset: dataset object that refer to the task
|
|
155
|
-
:return:
|
|
156
|
-
"""
|
|
157
|
-
if project is not None:
|
|
158
|
-
if project.id != _json.get('projectId', None):
|
|
159
|
-
logger.warning('Task has been fetched from a project that is not belong to it')
|
|
160
|
-
project = None
|
|
161
|
-
|
|
162
|
-
if dataset is not None:
|
|
163
|
-
if dataset.id != _json.get('datasetId', None):
|
|
164
|
-
logger.warning('Task has been fetched from a dataset that is not belong to it')
|
|
165
|
-
dataset = None
|
|
166
|
-
|
|
167
|
-
actions = [ItemAction.from_json(_json=action) for action in _json.get('availableActions', list())]
|
|
168
|
-
|
|
169
|
-
return cls(
|
|
170
|
-
name=_json.get('name', None),
|
|
171
|
-
status=_json.get('status', None),
|
|
172
|
-
project_id=_json.get('projectId', None),
|
|
173
|
-
metadata=_json.get('metadata', dict()),
|
|
174
|
-
url=_json.get('url', None),
|
|
175
|
-
spec=_json.get('spec', None),
|
|
176
|
-
id=_json['id'],
|
|
177
|
-
creator=_json.get('creator', None),
|
|
178
|
-
due_date=_json.get('dueDate', 0),
|
|
179
|
-
dataset_id=_json.get('datasetId', None),
|
|
180
|
-
recipe_id=_json.get('recipeId', None),
|
|
181
|
-
query=_json.get('query', None),
|
|
182
|
-
task_owner=_json.get('taskOwner', None),
|
|
183
|
-
item_status=_json.get('itemStatus', None),
|
|
184
|
-
assignmentIds=_json.get('assignmentIds', list()),
|
|
185
|
-
dataset=dataset,
|
|
186
|
-
project=project,
|
|
187
|
-
client_api=client_api,
|
|
188
|
-
annotation_status=_json.get('annotationStatus', None),
|
|
189
|
-
progress=_json.get('progress', None),
|
|
190
|
-
for_review=_json.get('forReview', None),
|
|
191
|
-
issues=_json.get('issues', None),
|
|
192
|
-
updated_at=_json.get('updatedAt', None),
|
|
193
|
-
created_at=_json.get('createdAt', None),
|
|
194
|
-
available_actions=actions,
|
|
195
|
-
total_items=_json.get('totalItems', None),
|
|
196
|
-
priority=_json.get('priority', None),
|
|
197
|
-
description=_json.get('description', None)
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
def to_json(self):
|
|
201
|
-
"""
|
|
202
|
-
Returns platform _json format of object
|
|
203
|
-
|
|
204
|
-
:return: platform json format of object
|
|
205
|
-
:rtype: dict
|
|
206
|
-
"""
|
|
207
|
-
_json = attr.asdict(
|
|
208
|
-
self, filter=attr.filters.exclude(
|
|
209
|
-
attr.fields(Task)._client_api,
|
|
210
|
-
attr.fields(Task)._project,
|
|
211
|
-
attr.fields(Task).project_id,
|
|
212
|
-
attr.fields(Task).dataset_id,
|
|
213
|
-
attr.fields(Task).recipe_id,
|
|
214
|
-
attr.fields(Task).task_owner,
|
|
215
|
-
attr.fields(Task).available_actions,
|
|
216
|
-
attr.fields(Task).item_status,
|
|
217
|
-
attr.fields(Task).due_date,
|
|
218
|
-
attr.fields(Task)._tasks,
|
|
219
|
-
attr.fields(Task)._dataset,
|
|
220
|
-
attr.fields(Task)._current_assignments,
|
|
221
|
-
attr.fields(Task)._assignments,
|
|
222
|
-
attr.fields(Task).annotation_status,
|
|
223
|
-
attr.fields(Task).for_review,
|
|
224
|
-
attr.fields(Task).issues,
|
|
225
|
-
attr.fields(Task).updated_at,
|
|
226
|
-
attr.fields(Task).created_at,
|
|
227
|
-
attr.fields(Task).total_items,
|
|
228
|
-
attr.fields(Task)._settings,
|
|
229
|
-
attr.fields(Task)._description
|
|
230
|
-
)
|
|
231
|
-
)
|
|
232
|
-
_json['projectId'] = self.project_id
|
|
233
|
-
_json['datasetId'] = self.dataset_id
|
|
234
|
-
_json['recipeId'] = self.recipe_id
|
|
235
|
-
_json['taskOwner'] = self.task_owner
|
|
236
|
-
_json['dueDate'] = self.due_date
|
|
237
|
-
_json['totalItems'] = self.total_items
|
|
238
|
-
_json['forReview'] = self.for_review
|
|
239
|
-
_json['description'] = self.description
|
|
240
|
-
|
|
241
|
-
if self.available_actions is not None:
|
|
242
|
-
_json['availableActions'] = [action.to_json() for action in self.available_actions]
|
|
243
|
-
|
|
244
|
-
return _json
|
|
245
|
-
|
|
246
|
-
@property
|
|
247
|
-
def platform_url(self):
|
|
248
|
-
return self._client_api._get_resource_url("projects/{}/tasks/{}".format(self.project.id, self.id))
|
|
249
|
-
|
|
250
|
-
@property
|
|
251
|
-
def current_assignments(self):
|
|
252
|
-
if self._current_assignments is None:
|
|
253
|
-
self._current_assignments = list()
|
|
254
|
-
for assignment in self.assignmentIds:
|
|
255
|
-
self._current_assignments.append(self.assignments.get(assignment_id=assignment))
|
|
256
|
-
return self._current_assignments
|
|
257
|
-
|
|
258
|
-
@property
|
|
259
|
-
def assignments(self):
|
|
260
|
-
if self._assignments is None:
|
|
261
|
-
self._assignments = repositories.Assignments(client_api=self._client_api, dataset=self._dataset,
|
|
262
|
-
project=self.project, task=self, project_id=self.project_id)
|
|
263
|
-
assert isinstance(self._assignments, repositories.Assignments)
|
|
264
|
-
return self._assignments
|
|
265
|
-
|
|
266
|
-
@property
|
|
267
|
-
def tasks(self):
|
|
268
|
-
if self._tasks is None:
|
|
269
|
-
self._tasks = repositories.Tasks(client_api=self._client_api, project=self.project, dataset=self.dataset)
|
|
270
|
-
assert isinstance(self._tasks, repositories.Tasks)
|
|
271
|
-
return self._tasks
|
|
272
|
-
|
|
273
|
-
@property
|
|
274
|
-
def settings(self):
|
|
275
|
-
if self._settings is None:
|
|
276
|
-
self._settings = repositories.Settings(
|
|
277
|
-
client_api=self._client_api,
|
|
278
|
-
project=self.project,
|
|
279
|
-
dataset=self.dataset,
|
|
280
|
-
task=self
|
|
281
|
-
)
|
|
282
|
-
assert isinstance(self._settings, repositories.Settings)
|
|
283
|
-
return self._settings
|
|
284
|
-
|
|
285
|
-
@property
|
|
286
|
-
def project(self):
|
|
287
|
-
if self._project is None:
|
|
288
|
-
self.get_project()
|
|
289
|
-
if self._project is None:
|
|
290
|
-
raise exceptions.PlatformException(error='2001',
|
|
291
|
-
message='Missing entity "project". need to "get_project()" ')
|
|
292
|
-
assert isinstance(self._project, entities.Project)
|
|
293
|
-
return self._project
|
|
294
|
-
|
|
295
|
-
@property
|
|
296
|
-
def dataset(self):
|
|
297
|
-
if self._dataset is None:
|
|
298
|
-
self.get_dataset()
|
|
299
|
-
if self._dataset is None:
|
|
300
|
-
raise exceptions.PlatformException(error='2001',
|
|
301
|
-
message='Missing entity "dataset". need to "get_dataset()" ')
|
|
302
|
-
assert isinstance(self._dataset, entities.Dataset)
|
|
303
|
-
return self._dataset
|
|
304
|
-
|
|
305
|
-
def get_project(self):
|
|
306
|
-
if self._project is None:
|
|
307
|
-
self._project = repositories.Projects(client_api=self._client_api).get(project_id=self.project_id)
|
|
308
|
-
|
|
309
|
-
def get_dataset(self):
|
|
310
|
-
if self._dataset is None:
|
|
311
|
-
self._dataset = repositories.Datasets(client_api=self._client_api, project=self._project).get(
|
|
312
|
-
dataset_id=self.dataset_id)
|
|
313
|
-
|
|
314
|
-
def open_in_web(self):
|
|
315
|
-
"""
|
|
316
|
-
Open the task in web platform
|
|
317
|
-
|
|
318
|
-
:return:
|
|
319
|
-
"""
|
|
320
|
-
self._client_api._open_in_web(url=self.platform_url)
|
|
321
|
-
|
|
322
|
-
def delete(self, wait=True):
|
|
323
|
-
"""
|
|
324
|
-
Delete task from platform
|
|
325
|
-
|
|
326
|
-
:param bool wait: wait until delete task finish
|
|
327
|
-
:return: True
|
|
328
|
-
:rtype: bool
|
|
329
|
-
"""
|
|
330
|
-
return self.tasks.delete(task_id=self.id, wait=wait)
|
|
331
|
-
|
|
332
|
-
def update(self, system_metadata=False):
|
|
333
|
-
"""
|
|
334
|
-
Update an Annotation Task
|
|
335
|
-
|
|
336
|
-
:param bool system_metadata: DEPRECATED
|
|
337
|
-
"""
|
|
338
|
-
return self.tasks.update(task=self, system_metadata=system_metadata)
|
|
339
|
-
|
|
340
|
-
def create_qa_task(self,
|
|
341
|
-
due_date,
|
|
342
|
-
assignee_ids,
|
|
343
|
-
filters=None,
|
|
344
|
-
items=None,
|
|
345
|
-
query=None,
|
|
346
|
-
workload=None,
|
|
347
|
-
metadata=None,
|
|
348
|
-
available_actions=None,
|
|
349
|
-
wait=True,
|
|
350
|
-
batch_size=None,
|
|
351
|
-
max_batch_workload=None,
|
|
352
|
-
allowed_assignees=None,
|
|
353
|
-
priority=TaskPriority.MEDIUM
|
|
354
|
-
):
|
|
355
|
-
"""
|
|
356
|
-
Create a new QA Task
|
|
357
|
-
|
|
358
|
-
:param float due_date: date by which the QA task should be finished; for example, due_date=datetime.datetime(day=1, month=1, year=2029).timestamp()
|
|
359
|
-
:param list assignee_ids: list the QA task assignees (contributors) that should be working on the task. Provide a list of users' emails
|
|
360
|
-
:param entities.Filters filters: dl.Filters entity to filter items for the task
|
|
361
|
-
:param List[entities.Item] items: list of items (item Id or objects) to insert to the task
|
|
362
|
-
:param dict DQL query: filter items for the task
|
|
363
|
-
:param List[WorkloadUnit] 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)]
|
|
364
|
-
:param dict metadata: metadata for the task
|
|
365
|
-
:param list available_actions: list of available actions (statuses) that will be available for the task items; The default statuses are: "approved" and "discard"
|
|
366
|
-
:param bool wait: wait until create task finish
|
|
367
|
-
:param int batch_size: Pulling batch size (items), use with pulling allocation method. Restrictions - Min 3, max 100
|
|
368
|
-
:param int max_batch_workload: Max items in assignment, use with pulling allocation method. Restrictions - Min batchSize + 2, max batchSize * 2
|
|
369
|
-
:param list allowed_assignees: list the task assignees (contributors) that should be working on the task. Provide a list of users' emails
|
|
370
|
-
:param entities.TaskPriority priority: priority of the task options in entities.TaskPriority
|
|
371
|
-
:return: task object
|
|
372
|
-
:rtype: dtlpy.entities.task.Task
|
|
373
|
-
|
|
374
|
-
**Example**:
|
|
375
|
-
|
|
376
|
-
.. code-block:: python
|
|
377
|
-
|
|
378
|
-
task = task.create_qa_task(due_date = datetime.datetime(day= 1, month= 1, year= 2029).timestamp(),
|
|
379
|
-
assignee_ids =[ 'annotator1@dataloop.ai', 'annotator2@dataloop.ai'])
|
|
380
|
-
"""
|
|
381
|
-
return self.tasks.create_qa_task(task=self,
|
|
382
|
-
due_date=due_date,
|
|
383
|
-
assignee_ids=assignee_ids,
|
|
384
|
-
filters=filters,
|
|
385
|
-
items=items,
|
|
386
|
-
query=query,
|
|
387
|
-
workload=workload,
|
|
388
|
-
metadata=metadata,
|
|
389
|
-
available_actions=available_actions,
|
|
390
|
-
wait=wait,
|
|
391
|
-
batch_size=batch_size,
|
|
392
|
-
max_batch_workload=max_batch_workload,
|
|
393
|
-
allowed_assignees=allowed_assignees,
|
|
394
|
-
priority=priority
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
def create_assignment(self, assignment_name, assignee_id, items=None, filters=None):
|
|
398
|
-
"""
|
|
399
|
-
Create a new assignment
|
|
400
|
-
|
|
401
|
-
:param str assignment_name: assignment name
|
|
402
|
-
:param str assignee_id: the assignment assignees (contributors) that should be working on the task. Provide a user email
|
|
403
|
-
:param List[entities.Item] items: list of items (item Id or objects) to insert to the task
|
|
404
|
-
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
405
|
-
:return: Assignment object
|
|
406
|
-
:rtype: dtlpy.entities.assignment.Assignment assignment
|
|
407
|
-
|
|
408
|
-
**Example**:
|
|
409
|
-
|
|
410
|
-
.. code-block:: python
|
|
411
|
-
|
|
412
|
-
assignment = task.create_assignment(assignee_id='annotator1@dataloop.ai')
|
|
413
|
-
"""
|
|
414
|
-
assignment = self.assignments.create(assignee_id=assignee_id,
|
|
415
|
-
filters=filters,
|
|
416
|
-
items=items)
|
|
417
|
-
|
|
418
|
-
assignment.metadata['system']['taskId'] = self.id
|
|
419
|
-
assignment.update(system_metadata=True)
|
|
420
|
-
self.assignmentIds.append(assignment.id)
|
|
421
|
-
self.update()
|
|
422
|
-
self.add_items(filters=filters, items=items)
|
|
423
|
-
return assignment
|
|
424
|
-
|
|
425
|
-
def add_items(self, filters=None, items=None, assignee_ids=None, workload=None, limit=None, wait=True, query=None):
|
|
426
|
-
"""
|
|
427
|
-
Add items to Task
|
|
428
|
-
|
|
429
|
-
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
430
|
-
:param list items: list of items (item Ids or objects) to add to the task
|
|
431
|
-
:param list assignee_ids: list to assignee who works in the task
|
|
432
|
-
:param list 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)]
|
|
433
|
-
:param int limit: the limit items that task can include
|
|
434
|
-
:param bool wait: wait until add items will to finish
|
|
435
|
-
:param dict query: query to filter the items for the task
|
|
436
|
-
|
|
437
|
-
:return: task entity
|
|
438
|
-
:rtype: dtlpy.entities.task.Task
|
|
439
|
-
"""
|
|
440
|
-
return self.tasks.add_items(task=self,
|
|
441
|
-
filters=filters,
|
|
442
|
-
items=items,
|
|
443
|
-
assignee_ids=assignee_ids,
|
|
444
|
-
workload=workload,
|
|
445
|
-
limit=limit,
|
|
446
|
-
wait=wait,
|
|
447
|
-
query=query)
|
|
448
|
-
|
|
449
|
-
def remove_items(self,
|
|
450
|
-
filters: entities.Filters = None,
|
|
451
|
-
query=None,
|
|
452
|
-
items=None,
|
|
453
|
-
wait=True):
|
|
454
|
-
"""
|
|
455
|
-
remove items from Task.
|
|
456
|
-
|
|
457
|
-
**Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned to be *owner* of the annotation task.
|
|
458
|
-
|
|
459
|
-
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
460
|
-
:param dict query: query to filter the items use it
|
|
461
|
-
:param list items: list of items to add to the task
|
|
462
|
-
:param bool wait: wait until remove items finish
|
|
463
|
-
|
|
464
|
-
:return: True if success and an error if failed
|
|
465
|
-
:rtype: bool
|
|
466
|
-
"""
|
|
467
|
-
return self.tasks.remove_items(task=self,
|
|
468
|
-
query=query,
|
|
469
|
-
filters=filters,
|
|
470
|
-
items=items,
|
|
471
|
-
wait=wait)
|
|
472
|
-
|
|
473
|
-
def get_items(self, filters=None, get_consensus_items: bool = False):
|
|
474
|
-
"""
|
|
475
|
-
Get the task items
|
|
476
|
-
|
|
477
|
-
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
478
|
-
:return: list of the items or PagedEntity output of items
|
|
479
|
-
:rtype: list or dtlpy.entities.paged_entities.PagedEntities
|
|
480
|
-
"""
|
|
481
|
-
return self.tasks.get_items(task_id=self.id, dataset=self.dataset, filters=filters, get_consensus_items=get_consensus_items)
|
|
482
|
-
|
|
483
|
-
def set_status(self, status: str, operation: str, item_ids: List[str]):
|
|
484
|
-
"""
|
|
485
|
-
Update item status within task
|
|
486
|
-
|
|
487
|
-
:param str status: string the describes the status
|
|
488
|
-
:param str operation: the status action need 'create' or 'delete'
|
|
489
|
-
:param list item_ids: List[str] id items ids
|
|
490
|
-
|
|
491
|
-
:return: True if success
|
|
492
|
-
:rtype: bool
|
|
493
|
-
"""
|
|
494
|
-
return self.tasks.set_status(status=status, operation=operation, item_ids=item_ids, task_id=self.id)
|
|
495
|
-
|
|
1
|
+
import traceback
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Union, List
|
|
4
|
+
import attr
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from .. import repositories, entities, exceptions
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(name='dtlpy')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AllocationMethod(str, Enum):
|
|
13
|
+
DISTRIBUTION = 'distribution'
|
|
14
|
+
PULLING = 'pulling'
|
|
15
|
+
|
|
16
|
+
class ConsensusTaskType(str, Enum):
|
|
17
|
+
CONSENSUS = 'consensus'
|
|
18
|
+
QUALIFICATION = 'qualification'
|
|
19
|
+
HONEYPOT = 'honeypot'
|
|
20
|
+
|
|
21
|
+
class TaskPriority(int, Enum):
|
|
22
|
+
LOW = 1
|
|
23
|
+
MEDIUM = 2
|
|
24
|
+
HIGH = 3
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ItemAction:
|
|
28
|
+
def __init__(self, action, display_name=None, color='#FFFFFF', icon=None):
|
|
29
|
+
if not action or not isinstance(action, str):
|
|
30
|
+
raise ValueError('action should be a non-empty string')
|
|
31
|
+
self.action = action
|
|
32
|
+
if not display_name:
|
|
33
|
+
display_name = action
|
|
34
|
+
self.display_name = display_name
|
|
35
|
+
self.color = color
|
|
36
|
+
self.icon = icon
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_json(cls, _json: dict):
|
|
40
|
+
kwarg = {
|
|
41
|
+
'action': _json.get('action')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if _json.get('displayName', False):
|
|
45
|
+
kwarg['display_name'] = _json['displayName']
|
|
46
|
+
|
|
47
|
+
if _json.get('color', False):
|
|
48
|
+
kwarg['color'] = _json['color']
|
|
49
|
+
|
|
50
|
+
if _json.get('icon', False):
|
|
51
|
+
kwarg['icon'] = _json['icon']
|
|
52
|
+
|
|
53
|
+
return cls(**kwarg)
|
|
54
|
+
|
|
55
|
+
def to_json(self) -> dict:
|
|
56
|
+
_json = {
|
|
57
|
+
'action': self.action,
|
|
58
|
+
'color': self.color,
|
|
59
|
+
'displayName': self.display_name if self.display_name is not None else self.action
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if self.icon is not None:
|
|
63
|
+
_json['icon'] = self.icon
|
|
64
|
+
|
|
65
|
+
return _json
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@attr.s
|
|
69
|
+
class Task:
|
|
70
|
+
"""
|
|
71
|
+
Task object
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
# platform
|
|
75
|
+
name = attr.ib()
|
|
76
|
+
status = attr.ib()
|
|
77
|
+
project_id = attr.ib()
|
|
78
|
+
metadata = attr.ib(repr=False)
|
|
79
|
+
id = attr.ib()
|
|
80
|
+
url = attr.ib(repr=False)
|
|
81
|
+
task_owner = attr.ib(repr=False)
|
|
82
|
+
item_status = attr.ib(repr=False)
|
|
83
|
+
creator = attr.ib()
|
|
84
|
+
due_date = attr.ib()
|
|
85
|
+
dataset_id = attr.ib()
|
|
86
|
+
spec = attr.ib()
|
|
87
|
+
recipe_id = attr.ib(repr=False)
|
|
88
|
+
query = attr.ib(repr=False)
|
|
89
|
+
assignmentIds = attr.ib(repr=False)
|
|
90
|
+
annotation_status = attr.ib(repr=False)
|
|
91
|
+
progress = attr.ib()
|
|
92
|
+
for_review = attr.ib()
|
|
93
|
+
issues = attr.ib()
|
|
94
|
+
updated_at = attr.ib()
|
|
95
|
+
created_at = attr.ib()
|
|
96
|
+
available_actions = attr.ib()
|
|
97
|
+
total_items = attr.ib()
|
|
98
|
+
priority = attr.ib()
|
|
99
|
+
_description = attr.ib()
|
|
100
|
+
|
|
101
|
+
# sdk
|
|
102
|
+
_client_api = attr.ib(repr=False)
|
|
103
|
+
_current_assignments = attr.ib(default=None, repr=False)
|
|
104
|
+
_assignments = attr.ib(default=None, repr=False)
|
|
105
|
+
_project = attr.ib(default=None, repr=False)
|
|
106
|
+
_dataset = attr.ib(default=None, repr=False)
|
|
107
|
+
_tasks = attr.ib(default=None, repr=False)
|
|
108
|
+
_settings = attr.ib(default=None, repr=False)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def description(self):
|
|
112
|
+
return self._description
|
|
113
|
+
|
|
114
|
+
@description.setter
|
|
115
|
+
def description(self, description):
|
|
116
|
+
if not isinstance(description, str):
|
|
117
|
+
raise ValueError('description should be a string')
|
|
118
|
+
if self._description is None:
|
|
119
|
+
self._description = {}
|
|
120
|
+
self._description['content'] = description
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _protected_from_json(_json, client_api, project=None, dataset=None):
|
|
124
|
+
"""
|
|
125
|
+
Same as from_json but with try-except to catch if error
|
|
126
|
+
|
|
127
|
+
:param dict _json: platform json that describe the task
|
|
128
|
+
:param client_api: ApiClient object
|
|
129
|
+
:param dtlpy.entities.project.Project project: project object where task will create
|
|
130
|
+
:param dtlpy.entities.dataset.Dataset dataset: dataset object that refer to the task
|
|
131
|
+
:return:
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
task = Task.from_json(
|
|
135
|
+
_json=_json,
|
|
136
|
+
client_api=client_api,
|
|
137
|
+
project=project,
|
|
138
|
+
dataset=dataset
|
|
139
|
+
)
|
|
140
|
+
status = True
|
|
141
|
+
except Exception:
|
|
142
|
+
task = traceback.format_exc()
|
|
143
|
+
status = False
|
|
144
|
+
return status, task
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def from_json(cls, _json, client_api, project=None, dataset=None):
|
|
148
|
+
"""
|
|
149
|
+
Return the task object form the json
|
|
150
|
+
|
|
151
|
+
:param dict _json: platform json that describe the task
|
|
152
|
+
:param client_api: ApiClient object
|
|
153
|
+
:param dtlpy.entities.project.Project project: project object where task will create
|
|
154
|
+
:param dtlpy.entities.dataset.Dataset dataset: dataset object that refer to the task
|
|
155
|
+
:return:
|
|
156
|
+
"""
|
|
157
|
+
if project is not None:
|
|
158
|
+
if project.id != _json.get('projectId', None):
|
|
159
|
+
logger.warning('Task has been fetched from a project that is not belong to it')
|
|
160
|
+
project = None
|
|
161
|
+
|
|
162
|
+
if dataset is not None:
|
|
163
|
+
if dataset.id != _json.get('datasetId', None):
|
|
164
|
+
logger.warning('Task has been fetched from a dataset that is not belong to it')
|
|
165
|
+
dataset = None
|
|
166
|
+
|
|
167
|
+
actions = [ItemAction.from_json(_json=action) for action in _json.get('availableActions', list())]
|
|
168
|
+
|
|
169
|
+
return cls(
|
|
170
|
+
name=_json.get('name', None),
|
|
171
|
+
status=_json.get('status', None),
|
|
172
|
+
project_id=_json.get('projectId', None),
|
|
173
|
+
metadata=_json.get('metadata', dict()),
|
|
174
|
+
url=_json.get('url', None),
|
|
175
|
+
spec=_json.get('spec', None),
|
|
176
|
+
id=_json['id'],
|
|
177
|
+
creator=_json.get('creator', None),
|
|
178
|
+
due_date=_json.get('dueDate', 0),
|
|
179
|
+
dataset_id=_json.get('datasetId', None),
|
|
180
|
+
recipe_id=_json.get('recipeId', None),
|
|
181
|
+
query=_json.get('query', None),
|
|
182
|
+
task_owner=_json.get('taskOwner', None),
|
|
183
|
+
item_status=_json.get('itemStatus', None),
|
|
184
|
+
assignmentIds=_json.get('assignmentIds', list()),
|
|
185
|
+
dataset=dataset,
|
|
186
|
+
project=project,
|
|
187
|
+
client_api=client_api,
|
|
188
|
+
annotation_status=_json.get('annotationStatus', None),
|
|
189
|
+
progress=_json.get('progress', None),
|
|
190
|
+
for_review=_json.get('forReview', None),
|
|
191
|
+
issues=_json.get('issues', None),
|
|
192
|
+
updated_at=_json.get('updatedAt', None),
|
|
193
|
+
created_at=_json.get('createdAt', None),
|
|
194
|
+
available_actions=actions,
|
|
195
|
+
total_items=_json.get('totalItems', None),
|
|
196
|
+
priority=_json.get('priority', None),
|
|
197
|
+
description=_json.get('description', None)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def to_json(self):
|
|
201
|
+
"""
|
|
202
|
+
Returns platform _json format of object
|
|
203
|
+
|
|
204
|
+
:return: platform json format of object
|
|
205
|
+
:rtype: dict
|
|
206
|
+
"""
|
|
207
|
+
_json = attr.asdict(
|
|
208
|
+
self, filter=attr.filters.exclude(
|
|
209
|
+
attr.fields(Task)._client_api,
|
|
210
|
+
attr.fields(Task)._project,
|
|
211
|
+
attr.fields(Task).project_id,
|
|
212
|
+
attr.fields(Task).dataset_id,
|
|
213
|
+
attr.fields(Task).recipe_id,
|
|
214
|
+
attr.fields(Task).task_owner,
|
|
215
|
+
attr.fields(Task).available_actions,
|
|
216
|
+
attr.fields(Task).item_status,
|
|
217
|
+
attr.fields(Task).due_date,
|
|
218
|
+
attr.fields(Task)._tasks,
|
|
219
|
+
attr.fields(Task)._dataset,
|
|
220
|
+
attr.fields(Task)._current_assignments,
|
|
221
|
+
attr.fields(Task)._assignments,
|
|
222
|
+
attr.fields(Task).annotation_status,
|
|
223
|
+
attr.fields(Task).for_review,
|
|
224
|
+
attr.fields(Task).issues,
|
|
225
|
+
attr.fields(Task).updated_at,
|
|
226
|
+
attr.fields(Task).created_at,
|
|
227
|
+
attr.fields(Task).total_items,
|
|
228
|
+
attr.fields(Task)._settings,
|
|
229
|
+
attr.fields(Task)._description
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
_json['projectId'] = self.project_id
|
|
233
|
+
_json['datasetId'] = self.dataset_id
|
|
234
|
+
_json['recipeId'] = self.recipe_id
|
|
235
|
+
_json['taskOwner'] = self.task_owner
|
|
236
|
+
_json['dueDate'] = self.due_date
|
|
237
|
+
_json['totalItems'] = self.total_items
|
|
238
|
+
_json['forReview'] = self.for_review
|
|
239
|
+
_json['description'] = self.description
|
|
240
|
+
|
|
241
|
+
if self.available_actions is not None:
|
|
242
|
+
_json['availableActions'] = [action.to_json() for action in self.available_actions]
|
|
243
|
+
|
|
244
|
+
return _json
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def platform_url(self):
|
|
248
|
+
return self._client_api._get_resource_url("projects/{}/tasks/{}".format(self.project.id, self.id))
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def current_assignments(self):
|
|
252
|
+
if self._current_assignments is None:
|
|
253
|
+
self._current_assignments = list()
|
|
254
|
+
for assignment in self.assignmentIds:
|
|
255
|
+
self._current_assignments.append(self.assignments.get(assignment_id=assignment))
|
|
256
|
+
return self._current_assignments
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def assignments(self):
|
|
260
|
+
if self._assignments is None:
|
|
261
|
+
self._assignments = repositories.Assignments(client_api=self._client_api, dataset=self._dataset,
|
|
262
|
+
project=self.project, task=self, project_id=self.project_id)
|
|
263
|
+
assert isinstance(self._assignments, repositories.Assignments)
|
|
264
|
+
return self._assignments
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def tasks(self):
|
|
268
|
+
if self._tasks is None:
|
|
269
|
+
self._tasks = repositories.Tasks(client_api=self._client_api, project=self.project, dataset=self.dataset)
|
|
270
|
+
assert isinstance(self._tasks, repositories.Tasks)
|
|
271
|
+
return self._tasks
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def settings(self):
|
|
275
|
+
if self._settings is None:
|
|
276
|
+
self._settings = repositories.Settings(
|
|
277
|
+
client_api=self._client_api,
|
|
278
|
+
project=self.project,
|
|
279
|
+
dataset=self.dataset,
|
|
280
|
+
task=self
|
|
281
|
+
)
|
|
282
|
+
assert isinstance(self._settings, repositories.Settings)
|
|
283
|
+
return self._settings
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def project(self):
|
|
287
|
+
if self._project is None:
|
|
288
|
+
self.get_project()
|
|
289
|
+
if self._project is None:
|
|
290
|
+
raise exceptions.PlatformException(error='2001',
|
|
291
|
+
message='Missing entity "project". need to "get_project()" ')
|
|
292
|
+
assert isinstance(self._project, entities.Project)
|
|
293
|
+
return self._project
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def dataset(self):
|
|
297
|
+
if self._dataset is None:
|
|
298
|
+
self.get_dataset()
|
|
299
|
+
if self._dataset is None:
|
|
300
|
+
raise exceptions.PlatformException(error='2001',
|
|
301
|
+
message='Missing entity "dataset". need to "get_dataset()" ')
|
|
302
|
+
assert isinstance(self._dataset, entities.Dataset)
|
|
303
|
+
return self._dataset
|
|
304
|
+
|
|
305
|
+
def get_project(self):
|
|
306
|
+
if self._project is None:
|
|
307
|
+
self._project = repositories.Projects(client_api=self._client_api).get(project_id=self.project_id)
|
|
308
|
+
|
|
309
|
+
def get_dataset(self):
|
|
310
|
+
if self._dataset is None:
|
|
311
|
+
self._dataset = repositories.Datasets(client_api=self._client_api, project=self._project).get(
|
|
312
|
+
dataset_id=self.dataset_id)
|
|
313
|
+
|
|
314
|
+
def open_in_web(self):
|
|
315
|
+
"""
|
|
316
|
+
Open the task in web platform
|
|
317
|
+
|
|
318
|
+
:return:
|
|
319
|
+
"""
|
|
320
|
+
self._client_api._open_in_web(url=self.platform_url)
|
|
321
|
+
|
|
322
|
+
def delete(self, wait=True):
|
|
323
|
+
"""
|
|
324
|
+
Delete task from platform
|
|
325
|
+
|
|
326
|
+
:param bool wait: wait until delete task finish
|
|
327
|
+
:return: True
|
|
328
|
+
:rtype: bool
|
|
329
|
+
"""
|
|
330
|
+
return self.tasks.delete(task_id=self.id, wait=wait)
|
|
331
|
+
|
|
332
|
+
def update(self, system_metadata=False):
|
|
333
|
+
"""
|
|
334
|
+
Update an Annotation Task
|
|
335
|
+
|
|
336
|
+
:param bool system_metadata: DEPRECATED
|
|
337
|
+
"""
|
|
338
|
+
return self.tasks.update(task=self, system_metadata=system_metadata)
|
|
339
|
+
|
|
340
|
+
def create_qa_task(self,
|
|
341
|
+
due_date,
|
|
342
|
+
assignee_ids,
|
|
343
|
+
filters=None,
|
|
344
|
+
items=None,
|
|
345
|
+
query=None,
|
|
346
|
+
workload=None,
|
|
347
|
+
metadata=None,
|
|
348
|
+
available_actions=None,
|
|
349
|
+
wait=True,
|
|
350
|
+
batch_size=None,
|
|
351
|
+
max_batch_workload=None,
|
|
352
|
+
allowed_assignees=None,
|
|
353
|
+
priority=TaskPriority.MEDIUM
|
|
354
|
+
):
|
|
355
|
+
"""
|
|
356
|
+
Create a new QA Task
|
|
357
|
+
|
|
358
|
+
:param float due_date: date by which the QA task should be finished; for example, due_date=datetime.datetime(day=1, month=1, year=2029).timestamp()
|
|
359
|
+
:param list assignee_ids: list the QA task assignees (contributors) that should be working on the task. Provide a list of users' emails
|
|
360
|
+
:param entities.Filters filters: dl.Filters entity to filter items for the task
|
|
361
|
+
:param List[entities.Item] items: list of items (item Id or objects) to insert to the task
|
|
362
|
+
:param dict DQL query: filter items for the task
|
|
363
|
+
:param List[WorkloadUnit] 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)]
|
|
364
|
+
:param dict metadata: metadata for the task
|
|
365
|
+
:param list available_actions: list of available actions (statuses) that will be available for the task items; The default statuses are: "approved" and "discard"
|
|
366
|
+
:param bool wait: wait until create task finish
|
|
367
|
+
:param int batch_size: Pulling batch size (items), use with pulling allocation method. Restrictions - Min 3, max 100
|
|
368
|
+
:param int max_batch_workload: Max items in assignment, use with pulling allocation method. Restrictions - Min batchSize + 2, max batchSize * 2
|
|
369
|
+
:param list allowed_assignees: list the task assignees (contributors) that should be working on the task. Provide a list of users' emails
|
|
370
|
+
:param entities.TaskPriority priority: priority of the task options in entities.TaskPriority
|
|
371
|
+
:return: task object
|
|
372
|
+
:rtype: dtlpy.entities.task.Task
|
|
373
|
+
|
|
374
|
+
**Example**:
|
|
375
|
+
|
|
376
|
+
.. code-block:: python
|
|
377
|
+
|
|
378
|
+
task = task.create_qa_task(due_date = datetime.datetime(day= 1, month= 1, year= 2029).timestamp(),
|
|
379
|
+
assignee_ids =[ 'annotator1@dataloop.ai', 'annotator2@dataloop.ai'])
|
|
380
|
+
"""
|
|
381
|
+
return self.tasks.create_qa_task(task=self,
|
|
382
|
+
due_date=due_date,
|
|
383
|
+
assignee_ids=assignee_ids,
|
|
384
|
+
filters=filters,
|
|
385
|
+
items=items,
|
|
386
|
+
query=query,
|
|
387
|
+
workload=workload,
|
|
388
|
+
metadata=metadata,
|
|
389
|
+
available_actions=available_actions,
|
|
390
|
+
wait=wait,
|
|
391
|
+
batch_size=batch_size,
|
|
392
|
+
max_batch_workload=max_batch_workload,
|
|
393
|
+
allowed_assignees=allowed_assignees,
|
|
394
|
+
priority=priority
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
def create_assignment(self, assignment_name, assignee_id, items=None, filters=None):
|
|
398
|
+
"""
|
|
399
|
+
Create a new assignment
|
|
400
|
+
|
|
401
|
+
:param str assignment_name: assignment name
|
|
402
|
+
:param str assignee_id: the assignment assignees (contributors) that should be working on the task. Provide a user email
|
|
403
|
+
:param List[entities.Item] items: list of items (item Id or objects) to insert to the task
|
|
404
|
+
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
405
|
+
:return: Assignment object
|
|
406
|
+
:rtype: dtlpy.entities.assignment.Assignment assignment
|
|
407
|
+
|
|
408
|
+
**Example**:
|
|
409
|
+
|
|
410
|
+
.. code-block:: python
|
|
411
|
+
|
|
412
|
+
assignment = task.create_assignment(assignee_id='annotator1@dataloop.ai')
|
|
413
|
+
"""
|
|
414
|
+
assignment = self.assignments.create(assignee_id=assignee_id,
|
|
415
|
+
filters=filters,
|
|
416
|
+
items=items)
|
|
417
|
+
|
|
418
|
+
assignment.metadata['system']['taskId'] = self.id
|
|
419
|
+
assignment.update(system_metadata=True)
|
|
420
|
+
self.assignmentIds.append(assignment.id)
|
|
421
|
+
self.update()
|
|
422
|
+
self.add_items(filters=filters, items=items)
|
|
423
|
+
return assignment
|
|
424
|
+
|
|
425
|
+
def add_items(self, filters=None, items=None, assignee_ids=None, workload=None, limit=None, wait=True, query=None):
|
|
426
|
+
"""
|
|
427
|
+
Add items to Task
|
|
428
|
+
|
|
429
|
+
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
430
|
+
:param list items: list of items (item Ids or objects) to add to the task
|
|
431
|
+
:param list assignee_ids: list to assignee who works in the task
|
|
432
|
+
:param list 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)]
|
|
433
|
+
:param int limit: the limit items that task can include
|
|
434
|
+
:param bool wait: wait until add items will to finish
|
|
435
|
+
:param dict query: query to filter the items for the task
|
|
436
|
+
|
|
437
|
+
:return: task entity
|
|
438
|
+
:rtype: dtlpy.entities.task.Task
|
|
439
|
+
"""
|
|
440
|
+
return self.tasks.add_items(task=self,
|
|
441
|
+
filters=filters,
|
|
442
|
+
items=items,
|
|
443
|
+
assignee_ids=assignee_ids,
|
|
444
|
+
workload=workload,
|
|
445
|
+
limit=limit,
|
|
446
|
+
wait=wait,
|
|
447
|
+
query=query)
|
|
448
|
+
|
|
449
|
+
def remove_items(self,
|
|
450
|
+
filters: entities.Filters = None,
|
|
451
|
+
query=None,
|
|
452
|
+
items=None,
|
|
453
|
+
wait=True):
|
|
454
|
+
"""
|
|
455
|
+
remove items from Task.
|
|
456
|
+
|
|
457
|
+
**Prerequisites**: You must be in the role of an *owner*, *developer*, or *annotation manager* who has been assigned to be *owner* of the annotation task.
|
|
458
|
+
|
|
459
|
+
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
460
|
+
:param dict query: query to filter the items use it
|
|
461
|
+
:param list items: list of items to add to the task
|
|
462
|
+
:param bool wait: wait until remove items finish
|
|
463
|
+
|
|
464
|
+
:return: True if success and an error if failed
|
|
465
|
+
:rtype: bool
|
|
466
|
+
"""
|
|
467
|
+
return self.tasks.remove_items(task=self,
|
|
468
|
+
query=query,
|
|
469
|
+
filters=filters,
|
|
470
|
+
items=items,
|
|
471
|
+
wait=wait)
|
|
472
|
+
|
|
473
|
+
def get_items(self, filters=None, get_consensus_items: bool = False):
|
|
474
|
+
"""
|
|
475
|
+
Get the task items
|
|
476
|
+
|
|
477
|
+
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
478
|
+
:return: list of the items or PagedEntity output of items
|
|
479
|
+
:rtype: list or dtlpy.entities.paged_entities.PagedEntities
|
|
480
|
+
"""
|
|
481
|
+
return self.tasks.get_items(task_id=self.id, dataset=self.dataset, filters=filters, get_consensus_items=get_consensus_items)
|
|
482
|
+
|
|
483
|
+
def set_status(self, status: str, operation: str, item_ids: List[str]):
|
|
484
|
+
"""
|
|
485
|
+
Update item status within task
|
|
486
|
+
|
|
487
|
+
:param str status: string the describes the status
|
|
488
|
+
:param str operation: the status action need 'create' or 'delete'
|
|
489
|
+
:param list item_ids: List[str] id items ids
|
|
490
|
+
|
|
491
|
+
:return: True if success
|
|
492
|
+
:rtype: bool
|
|
493
|
+
"""
|
|
494
|
+
return self.tasks.set_status(status=status, operation=operation, item_ids=item_ids, task_id=self.id)
|
|
495
|
+
|