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/assignment.py
CHANGED
|
@@ -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()
|