label-studio-sdk 1.0.0__py3-none-any.whl → 1.0.1__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.

Potentially problematic release.


This version of label-studio-sdk might be problematic. Click here for more details.

Files changed (87) hide show
  1. label_studio_sdk/__init__.py +30 -4
  2. label_studio_sdk/_extensions/pager_ext.py +49 -0
  3. label_studio_sdk/_legacy/objects.py +46 -7
  4. label_studio_sdk/_legacy/schema/label_config_schema.json +14 -14
  5. label_studio_sdk/actions/__init__.py +25 -0
  6. label_studio_sdk/actions/client.py +79 -6
  7. label_studio_sdk/actions/types/__init__.py +25 -0
  8. label_studio_sdk/actions/types/actions_create_request_filters.py +43 -0
  9. label_studio_sdk/actions/types/actions_create_request_filters_conjunction.py +5 -0
  10. label_studio_sdk/actions/types/actions_create_request_filters_items_item.py +49 -0
  11. label_studio_sdk/actions/types/actions_create_request_filters_items_item_filter.py +31 -0
  12. label_studio_sdk/actions/types/actions_create_request_filters_items_item_operator.py +23 -0
  13. label_studio_sdk/actions/types/actions_create_request_id.py +19 -0
  14. label_studio_sdk/actions/types/actions_create_request_ordering_item.py +31 -0
  15. label_studio_sdk/actions/types/actions_create_request_selected_items.py +10 -0
  16. label_studio_sdk/actions/types/actions_create_request_selected_items_excluded.py +39 -0
  17. label_studio_sdk/actions/types/actions_create_request_selected_items_included.py +39 -0
  18. label_studio_sdk/base_client.py +183 -0
  19. label_studio_sdk/client.py +17 -175
  20. label_studio_sdk/core/client_wrapper.py +1 -1
  21. label_studio_sdk/core/http_client.py +5 -1
  22. label_studio_sdk/export_storage/azure/client.py +176 -10
  23. label_studio_sdk/export_storage/azure/types/azure_create_response.py +15 -0
  24. label_studio_sdk/export_storage/azure/types/azure_update_response.py +15 -0
  25. label_studio_sdk/export_storage/gcs/client.py +180 -14
  26. label_studio_sdk/export_storage/gcs/types/gcs_create_response.py +16 -1
  27. label_studio_sdk/export_storage/gcs/types/gcs_update_response.py +16 -1
  28. label_studio_sdk/export_storage/local/client.py +168 -22
  29. label_studio_sdk/export_storage/local/types/local_create_response.py +12 -2
  30. label_studio_sdk/export_storage/local/types/local_update_response.py +12 -2
  31. label_studio_sdk/export_storage/redis/client.py +234 -30
  32. label_studio_sdk/export_storage/redis/types/redis_create_response.py +20 -5
  33. label_studio_sdk/export_storage/redis/types/redis_update_response.py +20 -5
  34. label_studio_sdk/export_storage/s3/client.py +214 -26
  35. label_studio_sdk/export_storage/s3/types/s3create_response.py +15 -0
  36. label_studio_sdk/export_storage/s3/types/s3update_response.py +15 -0
  37. label_studio_sdk/import_storage/azure/client.py +266 -90
  38. label_studio_sdk/import_storage/azure/types/azure_create_response.py +28 -18
  39. label_studio_sdk/import_storage/azure/types/azure_update_response.py +28 -18
  40. label_studio_sdk/import_storage/gcs/client.py +270 -94
  41. label_studio_sdk/import_storage/gcs/types/gcs_create_response.py +28 -18
  42. label_studio_sdk/import_storage/gcs/types/gcs_update_response.py +28 -18
  43. label_studio_sdk/import_storage/local/client.py +168 -22
  44. label_studio_sdk/import_storage/local/types/local_create_response.py +12 -2
  45. label_studio_sdk/import_storage/local/types/local_update_response.py +12 -2
  46. label_studio_sdk/import_storage/redis/client.py +206 -50
  47. label_studio_sdk/import_storage/redis/types/redis_create_response.py +20 -10
  48. label_studio_sdk/import_storage/redis/types/redis_update_response.py +20 -10
  49. label_studio_sdk/import_storage/s3/client.py +336 -110
  50. label_studio_sdk/import_storage/s3/types/s3create_response.py +35 -25
  51. label_studio_sdk/import_storage/s3/types/s3update_response.py +35 -25
  52. label_studio_sdk/{_legacy/label_interface → label_interface}/base.py +10 -0
  53. label_studio_sdk/{_legacy/label_interface → label_interface}/control_tags.py +109 -71
  54. label_studio_sdk/{_legacy/label_interface → label_interface}/interface.py +96 -50
  55. label_studio_sdk/{_legacy/label_interface → label_interface}/object_tags.py +8 -13
  56. label_studio_sdk/label_interface/region.py +75 -0
  57. label_studio_sdk/projects/client.py +4 -2
  58. label_studio_sdk/projects/client_ext.py +19 -0
  59. label_studio_sdk/tasks/client.py +26 -2
  60. label_studio_sdk/tasks/client_ext.py +18 -0
  61. label_studio_sdk/types/annotation.py +5 -5
  62. label_studio_sdk/types/task.py +1 -1
  63. label_studio_sdk/views/__init__.py +8 -4
  64. label_studio_sdk/views/types/__init__.py +8 -4
  65. label_studio_sdk/views/types/views_create_request_data.py +2 -2
  66. label_studio_sdk/views/types/views_create_request_data_filters.py +5 -5
  67. label_studio_sdk/views/types/views_create_request_data_filters_conjunction.py +1 -1
  68. label_studio_sdk/views/types/views_create_request_data_filters_items_item.py +10 -8
  69. label_studio_sdk/views/types/views_create_request_data_filters_items_item_filter.py +31 -0
  70. label_studio_sdk/views/types/views_create_request_data_filters_items_item_operator.py +23 -0
  71. label_studio_sdk/views/types/views_create_request_data_ordering_item.py +27 -34
  72. label_studio_sdk/views/types/views_update_request_data.py +2 -2
  73. label_studio_sdk/views/types/views_update_request_data_filters.py +5 -5
  74. label_studio_sdk/views/types/views_update_request_data_filters_conjunction.py +1 -1
  75. label_studio_sdk/views/types/views_update_request_data_filters_items_item.py +10 -8
  76. label_studio_sdk/views/types/views_update_request_data_filters_items_item_filter.py +31 -0
  77. label_studio_sdk/views/types/views_update_request_data_filters_items_item_operator.py +23 -0
  78. label_studio_sdk/views/types/views_update_request_data_ordering_item.py +27 -34
  79. {label_studio_sdk-1.0.0.dist-info → label_studio_sdk-1.0.1.dist-info}/METADATA +17 -161
  80. {label_studio_sdk-1.0.0.dist-info → label_studio_sdk-1.0.1.dist-info}/RECORD +84 -67
  81. label_studio_sdk/_legacy/label_interface/region.py +0 -43
  82. label_studio_sdk/views/types/views_create_request_data_ordering_item_direction.py +0 -5
  83. label_studio_sdk/views/types/views_update_request_data_ordering_item_direction.py +0 -5
  84. /label_studio_sdk/{_legacy/label_interface → label_interface}/__init__.py +0 -0
  85. /label_studio_sdk/{_legacy/label_interface → label_interface}/data_examples.json +0 -0
  86. /label_studio_sdk/{_legacy/label_interface → label_interface}/label_tags.py +0 -0
  87. {label_studio_sdk-1.0.0.dist-info → label_studio_sdk-1.0.1.dist-info}/WHEEL +0 -0
@@ -12,7 +12,7 @@ from typing import Dict, Optional, List, Tuple, Any, Callable, Union
12
12
  from pydantic import BaseModel
13
13
 
14
14
  # from typing import Dict, Optional, List, Tuple, Any
15
- from collections import defaultdict
15
+ from collections import defaultdict, OrderedDict
16
16
  from lxml import etree
17
17
  import xmljson
18
18
 
@@ -22,18 +22,18 @@ from label_studio_sdk._legacy.exceptions import (
22
22
  LabelStudioValidationErrorSentryIgnored,
23
23
  )
24
24
 
25
- from label_studio_sdk._legacy.label_interface.control_tags import (
25
+ from .control_tags import (
26
26
  ControlTag,
27
27
  ChoicesTag,
28
28
  LabelsTag,
29
29
  )
30
- from label_studio_sdk._legacy.label_interface.object_tags import ObjectTag
31
- from label_studio_sdk._legacy.label_interface.label_tags import LabelTag
30
+ from .object_tags import ObjectTag
31
+ from .label_tags import LabelTag
32
32
  from label_studio_sdk._legacy.objects import AnnotationValue, TaskValue, PredictionValue
33
33
 
34
34
 
35
35
  dir_path = os.path.dirname(os.path.realpath(__file__))
36
- file_path = os.path.join(dir_path, "..", "schema", "label_config_schema.json")
36
+ file_path = os.path.join(dir_path, "..", "_legacy", "schema", "label_config_schema.json")
37
37
 
38
38
  with open(file_path) as f:
39
39
  _LABEL_CONFIG_SCHEMA_DATA = json.load(f)
@@ -189,23 +189,34 @@ class LabelInterface:
189
189
  ```
190
190
  """
191
191
 
192
- def __init__(self, config: str, *args, **kwargs):
192
+ def __init__(self, config: str, tags_mapping=None, *args, **kwargs):
193
193
  """
194
- Create LabelInterface instance from the config string
195
- Example:
194
+ Initialize a LabelInterface instance using a config string.
195
+
196
+ Args:
197
+ config (str): Configuration string.
198
+ tags_mapping: Provide your own implementation of any tag, this is helpful in cases where you want to override one of the control tags, and have your custom .label() method implemented.
199
+
200
+ The configuration string should be formatted as follows:
201
+
196
202
  ```
197
- label_config = LabelInterface('''
198
203
  <View>
199
- <Choices name="sentiment" toName="txt">
204
+ <Choices name="sentiment" toName="txt">
200
205
  <Choice value="Positive" />
201
206
  <Choice value="Negative" />
202
207
  <Choice value="Neutral" />
203
- </Choices>
204
- <Text name="txt" value="$text" />
205
- ''')
208
+ </Choices>
209
+ <Text name="txt" value="$text" />
210
+ </View>
211
+ ```
212
+ This method will extract the predefined task from the configuration and
213
+ parse the controls, objects, and labels used in it.
206
214
  """
215
+ self.task_loaded = False
216
+
207
217
  self._config = config
208
-
218
+ self._tags_mapping = tags_mapping
219
+
209
220
  # extract predefined task from the config
210
221
  _task_data, _ann, _pred = LabelInterface.get_task_from_labeling_config(config)
211
222
  self._sample_config_task = _task_data
@@ -225,6 +236,8 @@ class LabelInterface:
225
236
  self._labels = labels
226
237
  self._tree = tree
227
238
 
239
+
240
+
228
241
  ##### NEW API
229
242
 
230
243
  @property
@@ -391,7 +404,38 @@ class LabelInterface:
391
404
  lst = list(filter(match_fn, lst))
392
405
 
393
406
  return lst
407
+
408
+ def load_task(self, task):
409
+ """Loads a task and substitutes the value in each object tag
410
+ with actual data from the task, returning a copy of the
411
+ LabelConfig object.
412
+
413
+ If the `value` field in an object tag is designed to take
414
+ variable input (i.e., `value_is_variable` is True), the
415
+ function replaces this value with the corresponding value from
416
+ the task dictionary.
417
+
418
+ Args:
419
+ task (dict): Dictionary representing the task, where
420
+ each key-value pair denotes an attribute-value of the
421
+ task.
422
+
423
+ Returns:
424
+ LabelInterface: A deep copy of the current LabelIntreface
425
+ instance with the object tags' value fields populated with
426
+ data from the task.
427
+
428
+ """
429
+ tree = copy.deepcopy(self)
430
+ tree.task_loaded = True
431
+
432
+ for obj in tree.objects:
433
+ print(obj.value_is_variable, obj.value_name)
434
+ if obj.value_is_variable and obj.value_name in task:
435
+ obj.value = task.get(obj.value_name)
394
436
 
437
+ return tree
438
+
395
439
  def parse(self, config_string: str) -> Tuple[Dict, Dict, Dict, etree._Element]:
396
440
  """Parses the received configuration string into dictionaries
397
441
  of ControlTags, ObjectTags, and Labels, along with an XML tree
@@ -422,10 +466,10 @@ class LabelInterface:
422
466
  variables.append(tag.attrib["indexFlag"])
423
467
 
424
468
  if ControlTag.validate_node(tag):
425
- controls[tag.attrib["name"]] = ControlTag.parse_node(tag)
469
+ controls[tag.attrib["name"]] = ControlTag.parse_node(tag, tags_mapping=self._tags_mapping)
426
470
 
427
471
  elif ObjectTag.validate_node(tag):
428
- objects[tag.attrib["name"]] = ObjectTag.parse_node(tag)
472
+ objects[tag.attrib["name"]] = ObjectTag.parse_node(tag, tags_mapping=self._tags_mapping)
429
473
 
430
474
  elif LabelTag.validate_node(tag):
431
475
  lb = LabelTag.parse_node(tag, controls)
@@ -488,34 +532,6 @@ class LabelInterface:
488
532
  "Label config contains non-unique names"
489
533
  )
490
534
 
491
- def load_task(self, task):
492
- """Loads a task and substitutes the value in each object tag
493
- with actual data from the task, returning a copy of the
494
- LabelConfig object.
495
-
496
- If the `value` field in an object tag is designed to take
497
- variable input (i.e., `value_is_variable` is True), the
498
- function replaces this value with the corresponding value from
499
- the task dictionary.
500
-
501
- Args:
502
- task (dict): Dictionary representing the task, where
503
- each key-value pair denotes an attribute-value of the
504
- task.
505
-
506
- Returns:
507
- LabelInterface: A deep copy of the current LabelIntreface
508
- instance with the object tags' value fields populated with
509
- data from the task.
510
-
511
- """
512
- tree = copy.deepcopy(self)
513
- for obj in tree.objects:
514
- if obj.value_is_variable and obj.value_name in task:
515
- obj.value = task.get(obj.value_name)
516
-
517
- return tree
518
-
519
535
  @property
520
536
  def is_valid(self):
521
537
  """ """
@@ -568,6 +584,23 @@ class LabelInterface:
568
584
 
569
585
  return True
570
586
 
587
+ def _validate_object(self, obj):
588
+ """ """
589
+ regions = []
590
+ for r in obj.get(RESULT_KEY):
591
+ if r.get('type') != "relation":
592
+ if not self.validate_region(r):
593
+ return False
594
+
595
+ regions.append(r)
596
+
597
+ for r in obj.get(RESULT_KEY):
598
+ if r.get('type') == "relation" and \
599
+ not self.validate_relation(r, regions):
600
+ return False
601
+
602
+ return True
603
+
571
604
  def validate_annotation(self, annotation):
572
605
  """Validates the given annotation against the current configuration.
573
606
 
@@ -586,11 +619,11 @@ class LabelInterface:
586
619
  validation, False otherwise.
587
620
 
588
621
  """
589
- return all(self.validate_region(r) for r in annotation.get(RESULT_KEY))
622
+ return self._validate_object(annotation)
590
623
 
591
624
  def validate_prediction(self, prediction):
592
625
  """Same as validate_annotation right now"""
593
- return all(self.validate_region(r) for r in prediction.get(RESULT_KEY))
626
+ return self._validate_object(prediction)
594
627
 
595
628
  def validate_region(self, region) -> bool:
596
629
  """Validates a region from the annotation against the current
@@ -620,22 +653,35 @@ class LabelInterface:
620
653
  if not control or not obj:
621
654
  return False
622
655
 
623
- # type of the region should match the tag name
656
+ # type of the region should match the tag name
624
657
  if control.tag.lower() != region["type"]:
625
658
  return False
626
-
659
+
627
660
  # make sure that in config it connects to the same tag as
628
661
  # immplied by the region data
629
662
  if region["to_name"] not in control.to_name:
630
663
  return False
631
-
664
+
632
665
  # validate the actual value, for example that <Labels /> tag
633
666
  # is producing start, end, and labels
634
667
  if not control.validate_value(region["value"]):
635
668
  return False
636
-
669
+
637
670
  return True
638
671
 
672
+ def validate_relation(self, relation, regions, _mapping=None) -> bool:
673
+ """Validates that the relation is correct and all the associated objects are provided"""
674
+ if _mapping is None:
675
+ _mapping = { r['id']: r for r in regions }
676
+
677
+ if relation.get("type") != "relation" or \
678
+ relation.get("direction") not in ("left", "right", "bi") or \
679
+ relation.get("from_id") not in _mapping or \
680
+ relation.get("to_id") not in _mapping:
681
+ return False
682
+
683
+ return True
684
+
639
685
  ### Generation
640
686
 
641
687
  def _sample_task(self, secure_mode=False):
@@ -8,7 +8,7 @@ import xml.etree.ElementTree
8
8
  from urllib.parse import urlencode
9
9
  from typing import Optional
10
10
 
11
- from .base import LabelStudioTag
11
+ from .base import LabelStudioTag, get_tag_class
12
12
 
13
13
  _TAG_TO_CLASS = {
14
14
  "audio": "AudioTag",
@@ -77,12 +77,6 @@ def data_examples(
77
77
  return _DATA_EXAMPLES[mode]
78
78
 
79
79
 
80
- def get_tag_class(name):
81
- """ """
82
- class_name = _TAG_TO_CLASS.get(name.lower())
83
- return globals().get(class_name, None)
84
-
85
-
86
80
  class ObjectTag(LabelStudioTag):
87
81
  """
88
82
  Class that represents a ObjectTag in Label Studio
@@ -103,7 +97,7 @@ class ObjectTag(LabelStudioTag):
103
97
  # self._value_type = value_type
104
98
 
105
99
  @classmethod
106
- def parse_node(cls, tag: xml.etree.ElementTree.Element) -> "ObjectTag":
100
+ def parse_node(cls, tag: xml.etree.ElementTree.Element, tags_mapping=None) -> "ObjectTag":
107
101
  """
108
102
  This class method parses a node and returns a ObjectTag object if the node has a name and a value.
109
103
 
@@ -117,8 +111,10 @@ class ObjectTag(LabelStudioTag):
117
111
  ObjectTag
118
112
  A new ObjectTag object with the tag name, attributes, name, and value.
119
113
  """
120
- tag_class = get_tag_class(tag.tag) or cls
121
-
114
+ tag_class = get_tag_class(tag.tag, _TAG_TO_CLASS, re_mapping=tags_mapping) or cls
115
+ if isinstance(tag_class, str):
116
+ tag_class = globals().get(tag_class, None)
117
+
122
118
  return tag_class(
123
119
  tag=tag.tag,
124
120
  attr=dict(tag.attrib),
@@ -146,10 +142,9 @@ class ObjectTag(LabelStudioTag):
146
142
  @property
147
143
  def value_is_variable(self) -> bool:
148
144
  """Check if value has variable"""
149
- pattern = re.compile(r"^\$[A-Za-z_]+$")
145
+ pattern = re.compile(r"^\$[^, ]+$")
150
146
  return bool(pattern.fullmatch(self.value))
151
-
152
- # TODO this should not be here as soon as we cover all the tags
147
+
153
148
 
154
149
  # and have generate_example in each
155
150
  def generate_example_value(self, mode="upload", secure_mode=False):
@@ -0,0 +1,75 @@
1
+ """
2
+ """
3
+
4
+ import json
5
+ from uuid import uuid4
6
+
7
+ from typing import Any, List, Dict, Optional
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class Region(BaseModel):
12
+ """
13
+ Class for Region Tag
14
+
15
+ Attributes:
16
+ -----------
17
+ id: str
18
+ The unique identifier of the region
19
+ x: int
20
+ The x coordinate of the region
21
+ y: int
22
+
23
+ """
24
+
25
+ id: str = Field(default_factory=lambda: str(uuid4()))
26
+ from_tag: Any
27
+ to_tag: Any
28
+ value: Any
29
+ relations: Optional[List[Dict]] = []
30
+
31
+ def _dict(self):
32
+ """ """
33
+ return {
34
+ "id": self.id,
35
+ "from_name": self.from_tag.name,
36
+ "to_name": self.to_tag.name,
37
+ "type": self.from_tag.tag.lower(),
38
+ # TODO This needs to be improved
39
+ "value": self.value.dict(),
40
+ }
41
+
42
+ def _dict_relations(self):
43
+ """ """
44
+ # this code does not include "labels" key if no labels were passed
45
+ return [
46
+ {**{
47
+ "from_id": self.id,
48
+ "to_id": rel.get("region", {}).id,
49
+ "type": "relation",
50
+ "direction": rel.get("direction", "right")},
51
+ **({"labels": rel["labels"]} if rel.get("labels") else {})
52
+ }
53
+ for rel in self.relations
54
+ ]
55
+
56
+ def to_json(self):
57
+ """ """
58
+ return json.dumps(self._dict())
59
+
60
+ def to_json_relations(self):
61
+ """ """
62
+ return json.dumps(self._dict_relations())
63
+
64
+ @property
65
+ def has_relations(self):
66
+ return len(self.relations) > 0
67
+
68
+ def add_relation(self, region=None, direction="right", label=None):
69
+ """ """
70
+ self.relations.append({ "region": region, "direction": direction, "labels": label })
71
+
72
+ def set_relations(self, rels):
73
+ """ """
74
+ self.relations = rels
75
+
@@ -82,6 +82,7 @@ class ProjectsClient:
82
82
  )
83
83
  client.projects.list()
84
84
  """
85
+ page = page or 1
85
86
  _response = self._client_wrapper.httpx_client.request(
86
87
  "api/projects/",
87
88
  method="GET",
@@ -95,7 +96,7 @@ class ProjectsClient:
95
96
  ordering=ordering,
96
97
  ids=ids,
97
98
  title=title,
98
- page=page + 1 if page is not None else 1,
99
+ page=page + 1,
99
100
  page_size=page_size,
100
101
  request_options=request_options,
101
102
  )
@@ -597,6 +598,7 @@ class AsyncProjectsClient:
597
598
  )
598
599
  await client.projects.list()
599
600
  """
601
+ page = page or 1
600
602
  _response = await self._client_wrapper.httpx_client.request(
601
603
  "api/projects/",
602
604
  method="GET",
@@ -610,7 +612,7 @@ class AsyncProjectsClient:
610
612
  ordering=ordering,
611
613
  ids=ids,
612
614
  title=title,
613
- page=page + 1 if page is not None else 1,
615
+ page=page + 1,
614
616
  page_size=page_size,
615
617
  request_options=request_options,
616
618
  )
@@ -0,0 +1,19 @@
1
+ from .client import ProjectsClient, AsyncProjectsClient
2
+
3
+ from label_studio_sdk._extensions.pager_ext import SyncPagerExt, AsyncPagerExt, T
4
+
5
+
6
+ class ProjectsClientExt(ProjectsClient):
7
+
8
+ def list(self, **kwargs) -> SyncPagerExt[T]:
9
+ return SyncPagerExt.from_sync_pager(super().list(**kwargs))
10
+
11
+ list.__doc__ = ProjectsClient.list.__doc__
12
+
13
+
14
+ class AsyncProjectsClientExt(AsyncProjectsClient):
15
+
16
+ async def list(self, **kwargs):
17
+ return await AsyncPagerExt.from_async_pager(await super().list(**kwargs))
18
+
19
+ list.__doc__ = AsyncProjectsClient.list.__doc__
@@ -126,6 +126,7 @@ class TasksClient:
126
126
  fields: typing.Optional[TasksListRequestFields] = None,
127
127
  review: typing.Optional[bool] = None,
128
128
  include: typing.Optional[str] = None,
129
+ query: typing.Optional[str] = None,
129
130
  request_options: typing.Optional[RequestOptions] = None,
130
131
  ) -> SyncPager[Task]:
131
132
  """
@@ -161,6 +162,14 @@ class TasksClient:
161
162
  include : typing.Optional[str]
162
163
  Specify which fields to include in the response
163
164
 
165
+ query : typing.Optional[str]
166
+ Additional query to filter tasks. It must be JSON encoded string of dict containing one of the following parameters: `{"filters": ..., "selectedItems": ..., "ordering": ...}`. Check Data Manager > Create View for more details about filters, selectedItems and ordering.
167
+
168
+ - **filters**: dict with `"conjunction"` string (`"or"` or `"and"`) and list of filters in `"items"` array. Each filter is a dictionary with keys: `"filter"`, `"operator"`, `"type"`, `"value"`. [Read more about available filters](https://labelstud.io/sdk/data_manager.html)<br/> Example: `{"conjunction": "or", "items": [{"filter": "filter:tasks:completed_at", "operator": "greater", "type": "Datetime", "value": "2021-01-01T00:00:00.000Z"}]}`
169
+ - **selectedItems**: dictionary with keys: `"all"`, `"included"`, `"excluded"`. If "all" is `false`, `"included"` must be used. If "all" is `true`, `"excluded"` must be used.<br/> Examples: `{"all": false, "included": [1, 2, 3]}` or `{"all": true, "excluded": [4, 5]}`
170
+ - **ordering**: list of fields to order by. Currently, ordering is supported by only one parameter. <br/>
171
+ Example: `["completed_at"]`
172
+
164
173
  request_options : typing.Optional[RequestOptions]
165
174
  Request-specific configuration.
166
175
 
@@ -178,6 +187,7 @@ class TasksClient:
178
187
  )
179
188
  client.tasks.list()
180
189
  """
190
+ page = page or 1
181
191
  _response = self._client_wrapper.httpx_client.request(
182
192
  "api/tasks/",
183
193
  method="GET",
@@ -190,6 +200,7 @@ class TasksClient:
190
200
  "fields": fields,
191
201
  "review": review,
192
202
  "include": include,
203
+ "query": query,
193
204
  },
194
205
  request_options=request_options,
195
206
  )
@@ -197,7 +208,7 @@ class TasksClient:
197
208
  _parsed_response = pydantic_v1.parse_obj_as(TasksListResponse, _response.json()) # type: ignore
198
209
  _has_next = True
199
210
  _get_next = lambda: self.list(
200
- page=page + 1 if page is not None else 1,
211
+ page=page + 1,
201
212
  page_size=page_size,
202
213
  view=view,
203
214
  project=project,
@@ -205,6 +216,7 @@ class TasksClient:
205
216
  fields=fields,
206
217
  review=review,
207
218
  include=include,
219
+ query=query,
208
220
  request_options=request_options,
209
221
  )
210
222
  _items = _parsed_response.tasks
@@ -522,6 +534,7 @@ class AsyncTasksClient:
522
534
  fields: typing.Optional[TasksListRequestFields] = None,
523
535
  review: typing.Optional[bool] = None,
524
536
  include: typing.Optional[str] = None,
537
+ query: typing.Optional[str] = None,
525
538
  request_options: typing.Optional[RequestOptions] = None,
526
539
  ) -> AsyncPager[Task]:
527
540
  """
@@ -557,6 +570,14 @@ class AsyncTasksClient:
557
570
  include : typing.Optional[str]
558
571
  Specify which fields to include in the response
559
572
 
573
+ query : typing.Optional[str]
574
+ Additional query to filter tasks. It must be JSON encoded string of dict containing one of the following parameters: `{"filters": ..., "selectedItems": ..., "ordering": ...}`. Check Data Manager > Create View for more details about filters, selectedItems and ordering.
575
+
576
+ - **filters**: dict with `"conjunction"` string (`"or"` or `"and"`) and list of filters in `"items"` array. Each filter is a dictionary with keys: `"filter"`, `"operator"`, `"type"`, `"value"`. [Read more about available filters](https://labelstud.io/sdk/data_manager.html)<br/> Example: `{"conjunction": "or", "items": [{"filter": "filter:tasks:completed_at", "operator": "greater", "type": "Datetime", "value": "2021-01-01T00:00:00.000Z"}]}`
577
+ - **selectedItems**: dictionary with keys: `"all"`, `"included"`, `"excluded"`. If "all" is `false`, `"included"` must be used. If "all" is `true`, `"excluded"` must be used.<br/> Examples: `{"all": false, "included": [1, 2, 3]}` or `{"all": true, "excluded": [4, 5]}`
578
+ - **ordering**: list of fields to order by. Currently, ordering is supported by only one parameter. <br/>
579
+ Example: `["completed_at"]`
580
+
560
581
  request_options : typing.Optional[RequestOptions]
561
582
  Request-specific configuration.
562
583
 
@@ -574,6 +595,7 @@ class AsyncTasksClient:
574
595
  )
575
596
  await client.tasks.list()
576
597
  """
598
+ page = page or 1
577
599
  _response = await self._client_wrapper.httpx_client.request(
578
600
  "api/tasks/",
579
601
  method="GET",
@@ -586,6 +608,7 @@ class AsyncTasksClient:
586
608
  "fields": fields,
587
609
  "review": review,
588
610
  "include": include,
611
+ "query": query,
589
612
  },
590
613
  request_options=request_options,
591
614
  )
@@ -593,7 +616,7 @@ class AsyncTasksClient:
593
616
  _parsed_response = pydantic_v1.parse_obj_as(TasksListResponse, _response.json()) # type: ignore
594
617
  _has_next = True
595
618
  _get_next = lambda: self.list(
596
- page=page + 1 if page is not None else 1,
619
+ page=page + 1,
597
620
  page_size=page_size,
598
621
  view=view,
599
622
  project=project,
@@ -601,6 +624,7 @@ class AsyncTasksClient:
601
624
  fields=fields,
602
625
  review=review,
603
626
  include=include,
627
+ query=query,
604
628
  request_options=request_options,
605
629
  )
606
630
  _items = _parsed_response.tasks
@@ -0,0 +1,18 @@
1
+ from .client import TasksClient, AsyncTasksClient
2
+ from label_studio_sdk._extensions.pager_ext import SyncPagerExt, AsyncPagerExt, T
3
+
4
+
5
+ class TasksClientExt(TasksClient):
6
+
7
+ def list(self, **kwargs) -> SyncPagerExt[T]:
8
+ return SyncPagerExt.from_sync_pager(super().list(**kwargs))
9
+
10
+ list.__doc__ = TasksClient.list.__doc__
11
+
12
+
13
+ class AsyncTasksClientExt(AsyncTasksClient):
14
+
15
+ async def list(self, **kwargs):
16
+ return await AsyncPagerExt.from_async_pager(await super().list(**kwargs))
17
+
18
+ list.__doc__ = AsyncTasksClient.list.__doc__
@@ -10,6 +10,11 @@ from .annotation_last_action import AnnotationLastAction
10
10
 
11
11
  class Annotation(pydantic_v1.BaseModel):
12
12
  id: typing.Optional[int] = None
13
+ result: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic_v1.Field(default=None)
14
+ """
15
+ List of annotation results for the task
16
+ """
17
+
13
18
  created_username: typing.Optional[str] = pydantic_v1.Field(default=None)
14
19
  """
15
20
  Username string
@@ -22,11 +27,6 @@ class Annotation(pydantic_v1.BaseModel):
22
27
 
23
28
  completed_by: typing.Optional[int] = None
24
29
  unique_id: typing.Optional[str] = None
25
- result: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field(default=None)
26
- """
27
- The main value of annotator work - labeling result in JSON format
28
- """
29
-
30
30
  was_cancelled: typing.Optional[bool] = pydantic_v1.Field(default=None)
31
31
  """
32
32
  User skipped the task
@@ -58,7 +58,7 @@ class Task(pydantic_v1.BaseModel):
58
58
  Completion time of the task
59
59
  """
60
60
 
61
- file_upload: typing.Optional[int] = pydantic_v1.Field(default=None)
61
+ file_upload: typing.Optional[str] = pydantic_v1.Field(default=None)
62
62
  """
63
63
  File upload ID for this task
64
64
  """
@@ -5,14 +5,16 @@ from .types import (
5
5
  ViewsCreateRequestDataFilters,
6
6
  ViewsCreateRequestDataFiltersConjunction,
7
7
  ViewsCreateRequestDataFiltersItemsItem,
8
+ ViewsCreateRequestDataFiltersItemsItemFilter,
9
+ ViewsCreateRequestDataFiltersItemsItemOperator,
8
10
  ViewsCreateRequestDataOrderingItem,
9
- ViewsCreateRequestDataOrderingItemDirection,
10
11
  ViewsUpdateRequestData,
11
12
  ViewsUpdateRequestDataFilters,
12
13
  ViewsUpdateRequestDataFiltersConjunction,
13
14
  ViewsUpdateRequestDataFiltersItemsItem,
15
+ ViewsUpdateRequestDataFiltersItemsItemFilter,
16
+ ViewsUpdateRequestDataFiltersItemsItemOperator,
14
17
  ViewsUpdateRequestDataOrderingItem,
15
- ViewsUpdateRequestDataOrderingItemDirection,
16
18
  )
17
19
 
18
20
  __all__ = [
@@ -20,12 +22,14 @@ __all__ = [
20
22
  "ViewsCreateRequestDataFilters",
21
23
  "ViewsCreateRequestDataFiltersConjunction",
22
24
  "ViewsCreateRequestDataFiltersItemsItem",
25
+ "ViewsCreateRequestDataFiltersItemsItemFilter",
26
+ "ViewsCreateRequestDataFiltersItemsItemOperator",
23
27
  "ViewsCreateRequestDataOrderingItem",
24
- "ViewsCreateRequestDataOrderingItemDirection",
25
28
  "ViewsUpdateRequestData",
26
29
  "ViewsUpdateRequestDataFilters",
27
30
  "ViewsUpdateRequestDataFiltersConjunction",
28
31
  "ViewsUpdateRequestDataFiltersItemsItem",
32
+ "ViewsUpdateRequestDataFiltersItemsItemFilter",
33
+ "ViewsUpdateRequestDataFiltersItemsItemOperator",
29
34
  "ViewsUpdateRequestDataOrderingItem",
30
- "ViewsUpdateRequestDataOrderingItemDirection",
31
35
  ]
@@ -4,26 +4,30 @@ from .views_create_request_data import ViewsCreateRequestData
4
4
  from .views_create_request_data_filters import ViewsCreateRequestDataFilters
5
5
  from .views_create_request_data_filters_conjunction import ViewsCreateRequestDataFiltersConjunction
6
6
  from .views_create_request_data_filters_items_item import ViewsCreateRequestDataFiltersItemsItem
7
+ from .views_create_request_data_filters_items_item_filter import ViewsCreateRequestDataFiltersItemsItemFilter
8
+ from .views_create_request_data_filters_items_item_operator import ViewsCreateRequestDataFiltersItemsItemOperator
7
9
  from .views_create_request_data_ordering_item import ViewsCreateRequestDataOrderingItem
8
- from .views_create_request_data_ordering_item_direction import ViewsCreateRequestDataOrderingItemDirection
9
10
  from .views_update_request_data import ViewsUpdateRequestData
10
11
  from .views_update_request_data_filters import ViewsUpdateRequestDataFilters
11
12
  from .views_update_request_data_filters_conjunction import ViewsUpdateRequestDataFiltersConjunction
12
13
  from .views_update_request_data_filters_items_item import ViewsUpdateRequestDataFiltersItemsItem
14
+ from .views_update_request_data_filters_items_item_filter import ViewsUpdateRequestDataFiltersItemsItemFilter
15
+ from .views_update_request_data_filters_items_item_operator import ViewsUpdateRequestDataFiltersItemsItemOperator
13
16
  from .views_update_request_data_ordering_item import ViewsUpdateRequestDataOrderingItem
14
- from .views_update_request_data_ordering_item_direction import ViewsUpdateRequestDataOrderingItemDirection
15
17
 
16
18
  __all__ = [
17
19
  "ViewsCreateRequestData",
18
20
  "ViewsCreateRequestDataFilters",
19
21
  "ViewsCreateRequestDataFiltersConjunction",
20
22
  "ViewsCreateRequestDataFiltersItemsItem",
23
+ "ViewsCreateRequestDataFiltersItemsItemFilter",
24
+ "ViewsCreateRequestDataFiltersItemsItemOperator",
21
25
  "ViewsCreateRequestDataOrderingItem",
22
- "ViewsCreateRequestDataOrderingItemDirection",
23
26
  "ViewsUpdateRequestData",
24
27
  "ViewsUpdateRequestDataFilters",
25
28
  "ViewsUpdateRequestDataFiltersConjunction",
26
29
  "ViewsUpdateRequestDataFiltersItemsItem",
30
+ "ViewsUpdateRequestDataFiltersItemsItemFilter",
31
+ "ViewsUpdateRequestDataFiltersItemsItemOperator",
27
32
  "ViewsUpdateRequestDataOrderingItem",
28
- "ViewsUpdateRequestDataOrderingItemDirection",
29
33
  ]