label-studio-sdk 1.0.2__py3-none-any.whl → 1.0.4__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 (67) hide show
  1. label_studio_sdk/__init__.py +20 -1
  2. label_studio_sdk/actions/client.py +8 -8
  3. label_studio_sdk/annotations/client.py +24 -24
  4. label_studio_sdk/base_client.py +3 -0
  5. label_studio_sdk/core/client_wrapper.py +1 -1
  6. label_studio_sdk/core/http_client.py +36 -8
  7. label_studio_sdk/core/request_options.py +2 -2
  8. label_studio_sdk/export_storage/__init__.py +2 -1
  9. label_studio_sdk/export_storage/azure/client.py +28 -28
  10. label_studio_sdk/export_storage/client.py +7 -4
  11. label_studio_sdk/export_storage/gcs/client.py +28 -28
  12. label_studio_sdk/export_storage/local/client.py +28 -28
  13. label_studio_sdk/export_storage/redis/client.py +28 -28
  14. label_studio_sdk/export_storage/s3/client.py +28 -28
  15. label_studio_sdk/export_storage/s3s/__init__.py +2 -0
  16. label_studio_sdk/export_storage/s3s/client.py +836 -0
  17. label_studio_sdk/files/client.py +24 -24
  18. label_studio_sdk/import_storage/__init__.py +2 -1
  19. label_studio_sdk/import_storage/azure/client.py +28 -28
  20. label_studio_sdk/import_storage/client.py +7 -4
  21. label_studio_sdk/import_storage/gcs/client.py +28 -28
  22. label_studio_sdk/import_storage/local/client.py +28 -28
  23. label_studio_sdk/import_storage/redis/client.py +28 -28
  24. label_studio_sdk/import_storage/s3/client.py +28 -28
  25. label_studio_sdk/import_storage/s3s/__init__.py +2 -0
  26. label_studio_sdk/import_storage/s3s/client.py +1054 -0
  27. label_studio_sdk/label_interface/base.py +2 -2
  28. label_studio_sdk/label_interface/control_tags.py +32 -18
  29. label_studio_sdk/label_interface/create.py +241 -0
  30. label_studio_sdk/label_interface/interface.py +68 -0
  31. label_studio_sdk/label_interface/object_tags.py +26 -10
  32. label_studio_sdk/label_interface/objects.py +5 -5
  33. label_studio_sdk/ml/client.py +36 -36
  34. label_studio_sdk/predictions/client.py +24 -24
  35. label_studio_sdk/projects/__init__.py +8 -2
  36. label_studio_sdk/projects/client.py +232 -69
  37. label_studio_sdk/projects/client_ext.py +16 -1
  38. label_studio_sdk/projects/exports/client.py +38 -38
  39. label_studio_sdk/projects/types/__init__.py +2 -1
  40. label_studio_sdk/projects/types/projects_update_response.py +96 -0
  41. label_studio_sdk/tasks/client.py +70 -60
  42. label_studio_sdk/tasks/client_ext.py +4 -0
  43. label_studio_sdk/types/__init__.py +16 -0
  44. label_studio_sdk/types/base_task.py +4 -2
  45. label_studio_sdk/types/base_task_file_upload.py +5 -0
  46. label_studio_sdk/types/base_task_updated_by.py +5 -0
  47. label_studio_sdk/types/data_manager_task_serializer.py +3 -2
  48. label_studio_sdk/types/data_manager_task_serializer_annotators_item.py +5 -0
  49. label_studio_sdk/types/s3s_export_storage.py +80 -0
  50. label_studio_sdk/types/s3s_import_storage.py +129 -0
  51. label_studio_sdk/types/s3s_import_storage_status.py +7 -0
  52. label_studio_sdk/types/task.py +3 -2
  53. label_studio_sdk/types/task_annotators_item.py +5 -0
  54. label_studio_sdk/types/workspace.py +77 -0
  55. label_studio_sdk/users/client.py +32 -32
  56. label_studio_sdk/views/client.py +24 -24
  57. label_studio_sdk/webhooks/client.py +24 -24
  58. label_studio_sdk/workspaces/__init__.py +6 -0
  59. label_studio_sdk/workspaces/client.py +569 -0
  60. label_studio_sdk/workspaces/members/__init__.py +5 -0
  61. label_studio_sdk/workspaces/members/client.py +297 -0
  62. label_studio_sdk/workspaces/members/types/__init__.py +6 -0
  63. label_studio_sdk/workspaces/members/types/members_create_response.py +32 -0
  64. label_studio_sdk/workspaces/members/types/members_list_response_item.py +32 -0
  65. {label_studio_sdk-1.0.2.dist-info → label_studio_sdk-1.0.4.dist-info}/METADATA +11 -12
  66. {label_studio_sdk-1.0.2.dist-info → label_studio_sdk-1.0.4.dist-info}/RECORD +67 -46
  67. {label_studio_sdk-1.0.2.dist-info → label_studio_sdk-1.0.4.dist-info}/WHEEL +0 -0
@@ -24,8 +24,8 @@ class LabelStudioTag(BaseModel):
24
24
  The tag name
25
25
  """
26
26
 
27
- attr: Optional[Dict]
28
- tag: Optional[str]
27
+ attr: Optional[Dict] = {}
28
+ tag: Optional[str] = ""
29
29
 
30
30
  def match(
31
31
  self,
@@ -145,6 +145,14 @@ class ControlTag(LabelStudioTag):
145
145
 
146
146
  return tag_class(**tag_info)
147
147
 
148
+ def collect_attrs(self):
149
+ """Return tag attrs as a single dict"""
150
+ return {
151
+ **self.attr,
152
+ "name": self.name,
153
+ "toName": self.to_name
154
+ }
155
+
148
156
  def get_object(self, name=None):
149
157
  """
150
158
  This method retrieves the object tag that the control tag maps to.
@@ -473,7 +481,7 @@ class ChoicesValue(BaseModel):
473
481
 
474
482
  class ChoicesTag(ControlTag):
475
483
  """ """
476
-
484
+ tag: str = "Choices"
477
485
  _label_attr_name: str = "choices"
478
486
  _value_class: Type[ChoicesValue] = ChoicesValue
479
487
 
@@ -484,7 +492,7 @@ class LabelsValue(SpanSelection):
484
492
 
485
493
  class LabelsTag(ControlTag):
486
494
  """ """
487
-
495
+ tag: str = "Labels"
488
496
  _label_attr_name: str = "labels"
489
497
  _value_class: Type[LabelsValue] = LabelsValue
490
498
 
@@ -534,7 +542,7 @@ class BrushLabelsValue(BrushValue):
534
542
 
535
543
  class BrushTag(ControlTag):
536
544
  """ """
537
-
545
+ tag: str = "Brush"
538
546
  _value_class: Type[BrushValue] = BrushValue
539
547
 
540
548
  # def validate_value(self, value) -> bool:
@@ -546,7 +554,7 @@ class BrushTag(ControlTag):
546
554
 
547
555
  class BrushLabelsTag(BrushTag):
548
556
  """ """
549
-
557
+ tag: str = "BrushLabels"
550
558
  _label_attr_name: str = "brushlabels"
551
559
  _value_class: Type[BrushLabelsValue] = BrushLabelsValue
552
560
 
@@ -565,13 +573,13 @@ class EllipseLabelsValue(EllipseValue):
565
573
 
566
574
  class EllipseTag(ControlTag):
567
575
  """ """
568
-
576
+ tag: str = "Ellipse"
569
577
  _value_class: Type[EllipseValue] = EllipseValue
570
578
 
571
579
 
572
580
  class EllipseLabelsTag(ControlTag):
573
581
  """ """
574
-
582
+ tag: str = "EllipseLabels"
575
583
  _label_attr_name: str = "ellipselabels"
576
584
  _value_class: Type[EllipseLabelsValue] = EllipseLabelsValue
577
585
 
@@ -587,13 +595,13 @@ class KeyPointLabelsValue(KeyPointValue):
587
595
 
588
596
  class KeyPointTag(ControlTag):
589
597
  """ """
590
-
598
+ tag: str = "KeyPoint"
591
599
  _value_class: Type[KeyPointValue] = KeyPointValue
592
600
 
593
601
 
594
602
  class KeyPointLabelsTag(ControlTag):
595
603
  """ """
596
-
604
+ tag: str = "KeyPointLabels"
597
605
  _label_attr_name: str = "keypointlabels"
598
606
  _value_class: Type[KeyPointLabelsValue] = KeyPointLabelsValue
599
607
 
@@ -608,13 +616,13 @@ class PolygonLabelsValue(PolygonValue):
608
616
 
609
617
  class PolygonTag(ControlTag):
610
618
  """ """
611
-
619
+ tag: str = "Polygon"
612
620
  _value_class: Type[PolygonValue] = PolygonValue
613
621
 
614
622
 
615
623
  class PolygonLabelsTag(ControlTag):
616
624
  """ """
617
-
625
+ tag: str = "PolygonLabels"
618
626
  _label_attr_name: str = "polygonlabels"
619
627
  _value_class: Type[PolygonLabelsValue] = PolygonLabelsValue
620
628
 
@@ -633,13 +641,13 @@ class RectangleLabelsValue(RectangleValue):
633
641
 
634
642
  class RectangleTag(ControlTag):
635
643
  """ """
636
-
644
+ tag: str = "Rectangle"
637
645
  _value_class: Type[RectangleValue] = RectangleValue
638
646
 
639
647
 
640
648
  class RectangleLabelsTag(ControlTag):
641
649
  """ """
642
-
650
+ tag: str = "RectangleLabels"
643
651
  _label_attr_name: str = "rectanglelabels"
644
652
  _value_class: Type[RectangleLabelsValue] = RectangleLabelsValue
645
653
 
@@ -663,6 +671,7 @@ class VideoRectangleValue(BaseModel):
663
671
 
664
672
  class VideoRectangleTag(ControlTag):
665
673
  """ """
674
+ tag: str = "VideoRectangle"
666
675
  _label_attr_name: str = "labels"
667
676
  _value_class: Type[VideoRectangleValue] = VideoRectangleValue
668
677
 
@@ -673,6 +682,7 @@ class NumberValue(BaseModel):
673
682
 
674
683
  class NumberTag(ControlTag):
675
684
  """ """
685
+ tag: str = "Number"
676
686
  _value_class: Type[NumberValue] = NumberValue
677
687
 
678
688
 
@@ -682,6 +692,7 @@ class DateTimeValue(BaseModel):
682
692
 
683
693
  class DateTimeTag(ControlTag):
684
694
  """ """
695
+ tag: str = "DateTime"
685
696
  _value_class: Type[DateTimeValue] = DateTimeValue
686
697
 
687
698
 
@@ -691,7 +702,7 @@ class HyperTextLabelsValue(SpanSelectionOffsets):
691
702
 
692
703
  class HyperTextLabelsTag(ControlTag):
693
704
  """ """
694
-
705
+ tag: str = "HyperTextLabels"
695
706
  _label_attr_name: str = "htmllabels"
696
707
  _value_class: Type[HyperTextLabelsValue] = HyperTextLabelsValue
697
708
 
@@ -702,7 +713,7 @@ class PairwiseValue(BaseModel):
702
713
 
703
714
  class PairwiseTag(ControlTag):
704
715
  """ """
705
-
716
+ tag: str = "Pairwise"
706
717
  _value_class: Type[PairwiseValue] = PairwiseValue
707
718
 
708
719
  def label(self, side):
@@ -717,7 +728,7 @@ class ParagraphLabelsValue(SpanSelectionOffsets):
717
728
 
718
729
  class ParagraphLabelsTag(ControlTag):
719
730
  """ """
720
-
731
+ tag: str = "ParagraphsLabels"
721
732
  _label_attr_name: str = "paragraphlabels"
722
733
  _value_class: Type[ParagraphLabelsValue] = ParagraphLabelsValue
723
734
 
@@ -736,6 +747,7 @@ class RankerValue(BaseModel):
736
747
 
737
748
  class RankerTag(ControlTag):
738
749
  """ """
750
+ tag: str = "Ranker"
739
751
  _value_class: Type[RankerValue] = RankerValue
740
752
 
741
753
 
@@ -745,11 +757,13 @@ class RatingValue(BaseModel):
745
757
 
746
758
  class RatingTag(ControlTag):
747
759
  """ """
760
+ tag: str = "Rating"
748
761
  _value_class: Type[RatingValue] = RatingValue
749
762
 
750
763
 
751
764
  class RelationsTag(ControlTag):
752
765
  """ """
766
+ tag: str = "Relations"
753
767
  def validate_value(self, ) -> bool:
754
768
  """ """
755
769
  raise NotImplemented("""Should not be called directly, instead
@@ -768,7 +782,7 @@ class TaxonomyValue(BaseModel):
768
782
 
769
783
  class TaxonomyTag(ControlTag):
770
784
  """ """
771
-
785
+ tag: str = "Taxonomy"
772
786
  _value_class: Type[TaxonomyValue] = TaxonomyValue
773
787
 
774
788
 
@@ -778,7 +792,7 @@ class TextAreaValue(BaseModel):
778
792
 
779
793
  class TextAreaTag(ControlTag):
780
794
  """ """
781
-
795
+ tag: str = "TextArea"
782
796
  _value_class: Type[TextAreaValue] = TextAreaValue
783
797
 
784
798
 
@@ -789,6 +803,6 @@ class TimeSeriesValue(SpanSelection):
789
803
 
790
804
  class TimeSeriesLabelsTag(ControlTag):
791
805
  """ """
792
-
806
+ tag: str = "TimeSeriesLabels"
793
807
  _label_attr_name: str = "timeserieslabels"
794
808
  _value_class: Type[TimeSeriesValue] = TimeSeriesValue
@@ -0,0 +1,241 @@
1
+ """
2
+ """
3
+ from typing import Dict, Optional, List, Tuple, Any, Callable, Union
4
+ import xml.etree.ElementTree as ET
5
+ from xml.dom import minidom
6
+
7
+ from label_studio_sdk.label_interface.base import LabelStudioTag
8
+
9
+ import label_studio_sdk.label_interface.object_tags as OT
10
+ import label_studio_sdk.label_interface.control_tags as CT
11
+
12
+ ## syntatic sugar helper functions
13
+
14
+ def labels(labels, tag_type="Labels", **kwargs):
15
+ """
16
+ Converts labels to a tuple structure.
17
+
18
+ Args:
19
+ labels: The labels to be converted.
20
+ tag_type: Tag type to assign to each label.
21
+
22
+ Returns:
23
+ A tuple containing labels as per the tag type.
24
+ """
25
+ return (tag_type, kwargs, _convert_to_tuple(labels, tag_type="Label"))
26
+
27
+
28
+ def taxonomy(labels, **kwargs):
29
+ """
30
+ Constructs a taxonomy.
31
+
32
+ Args:
33
+ labels: The labels to construct the taxonomy.
34
+
35
+ Returns:
36
+ A tuple containing the taxonomy.
37
+ """
38
+ result = []
39
+
40
+ for arg in labels:
41
+ if isinstance(arg, tuple):
42
+ parent, *children = _convert_to_tuple(arg)
43
+ parent_children = (parent[0], parent[1], tuple(children))
44
+ result.append(parent_children)
45
+ else:
46
+ result.append(("Choice", {"value": arg}, {}))
47
+
48
+ return ("Taxonomy", kwargs, tuple(result))
49
+
50
+ def choices(labels, **kwargs):
51
+ """
52
+ Constructs a choices tuple.
53
+
54
+ Args:
55
+ labels: The labels to be converted.
56
+
57
+ Returns:
58
+ A tuple containing the choices.
59
+ """
60
+ return ("Choices", kwargs, _convert_to_tuple(labels))
61
+
62
+
63
+ def _prettify(element):
64
+ """
65
+ Returns a pretty-printed XML string for the Element.
66
+ """
67
+ rough_string = ET.tostring(element, 'utf-8')
68
+ reparsed = minidom.parseString(rough_string)
69
+ return reparsed.toprettyxml(indent=" ")
70
+
71
+
72
+ def _convert_to_tuple(args, tag_type="Choice"):
73
+ """
74
+ Converts arguments to a tuple structure of choices.
75
+
76
+ Args:
77
+ args: The labels to be converted.
78
+ tag_type: Tag type to assign to each label.
79
+
80
+ Returns:
81
+ A tuple containing all labels in specified format.
82
+ """
83
+ if not args:
84
+ return None
85
+
86
+ return tuple(((tag_type, {"value": arg}, {})) for arg in args)
87
+
88
+
89
+ def _create_nested_elements(parent: ET.Element, elements: List[Union[str, Dict[str, str], List]]) -> None:
90
+ """
91
+ Adds nested elements to the parent element in the Element Tree.
92
+
93
+ Args:
94
+ parent: The parent ET.Element object.
95
+ elements: A list containing element definitions.
96
+ """
97
+ for element in elements:
98
+ if isinstance(element, str):
99
+ ET.SubElement(parent, element)
100
+ elif isinstance(element, dict):
101
+ for k, v in element.items():
102
+ parent.set(k, v)
103
+ elif isinstance(element, list) or isinstance(element, tuple):
104
+ create_element(element, parent=parent)
105
+
106
+
107
+ def _convert(name: str, tag: Union[str, list, tuple, LabelStudioTag]) -> tuple:
108
+ """
109
+ Converts tags from str, list, tuple, or class instance to a tuple format.
110
+
111
+ Args:
112
+ name: Name of the tag.
113
+ tag: Tag in str, list, tuple or class instance format.
114
+
115
+ Returns:
116
+ A tuple version of the input tag.
117
+
118
+ Raises:
119
+ TypeError: If input tag is not a str, list, tuple, or LabelStudioTag.
120
+ """
121
+
122
+ if isinstance(tag, LabelStudioTag):
123
+ tag.name = tag.name or name
124
+ child_tag_type = "Choice" if tag.tag in ["Choices", "Taxonomy"] else "Label"
125
+ el = tag.tag, tag.collect_attrs(), _convert_to_tuple(getattr(tag, "labels", None), tag_type=child_tag_type)
126
+ elif isinstance(tag, (list, tuple)):
127
+ el = (*tag, ()) if len(tag) < 3 else tag
128
+ elif isinstance(tag, str):
129
+ el = tag, {}, ()
130
+ else:
131
+ raise TypeError("Input tag must be one of str, list, tuple, LabelStudioTag")
132
+
133
+ el[1].setdefault("name", name)
134
+
135
+ if el[0].lower() in OT._TAG_TO_CLASS and not el[1].get("value"):
136
+ el[1]["value"] = "$" + name
137
+
138
+ return el
139
+
140
+
141
+ def _find_first_object_tag(tags):
142
+ """
143
+ Finds the first object tag in the input tags dictionary.
144
+
145
+ Args:
146
+ tags: A dictionary of tags.
147
+
148
+ Returns:
149
+ The first object tag found, or None.
150
+ """
151
+ for name, tag in tags.items():
152
+ tag_tuple = _convert(name, tag)
153
+ tag_name = tag_tuple[0]
154
+
155
+ if tag_name.lower() in OT._TAG_TO_CLASS:
156
+ return tag_tuple
157
+
158
+
159
+ def create_element(element: List[Union[str, Dict[str, str], List]], parent=None) -> ET.Element:
160
+ """
161
+ Creates an XML element.
162
+
163
+ Args:
164
+ element: List of tag, attributes and children to feed into the element.
165
+ parent: Parent element to append the new element to (Optional).
166
+
167
+ Returns:
168
+ An ElementTree element.
169
+ """
170
+ tag, attrs, children = element
171
+ el = ET.SubElement(parent, tag) if parent is not None else ET.Element(tag)
172
+
173
+ for k, v in attrs.items():
174
+ el.set(k, v)
175
+
176
+ if children:
177
+ _create_nested_elements(el, children)
178
+
179
+ return el
180
+
181
+
182
+ def tree_from_tuples(*elements):
183
+ """
184
+ Creates an ElementTree from the input element definitions, and return a string for it.
185
+
186
+ Args:
187
+ elements: Variable length argument list of elements to add to the ElementTree.
188
+
189
+ Returns:
190
+ XML configuration
191
+ """
192
+ view = ET.Element('View')
193
+ for element in elements:
194
+ el = create_element(element)
195
+ view.append(el)
196
+
197
+ return view
198
+
199
+
200
+ def tree_to_string(tree, pretty=True):
201
+ """
202
+ """
203
+ if pretty:
204
+ pp = _prettify(tree)
205
+ return pp.replace('<?xml version="1.0" ?>\n', '')
206
+ else:
207
+ return ET.tostring(tree, encoding='unicode')
208
+
209
+
210
+ def convert_tags_description(tags: Dict[str, Any],
211
+ mapping: Optional[Dict[str, str]]=None) -> List[Union[str, Dict[str, str], List]]:
212
+ """
213
+ Convert tags into a structured format.
214
+
215
+ Args:
216
+ tags: A dictionary of tags.
217
+ mapping: Optional mapping of tag name transformations.
218
+
219
+ Returns:
220
+ A list of the structured tags.
221
+ """
222
+ elements = []
223
+ first_object_tag = _find_first_object_tag(tags)
224
+
225
+ for name, tag in tags.items():
226
+ el = _convert(name, tag)
227
+
228
+ # Set `toName` property of the tag if tag name is in
229
+ # CT._TAG_TO_CLASS and `toName` key is not in tag attributes
230
+ # The value of `toName` key is set based on whether `name` of
231
+ # tag is in the mapping dictionary or the `name` of the first
232
+ # object tag if it's not in mapping
233
+ if el[0].lower() in CT._TAG_TO_CLASS and ("toName" not in el[1] or el[1]["toName"] is None):
234
+ if mapping and el[1].get("name") in mapping:
235
+ el[1]["toName"] = mapping.get(el[1]["name"])
236
+ else:
237
+ el[1]["toName"] = first_object_tag[1]["name"]
238
+
239
+ elements.append(el)
240
+
241
+ return elements
@@ -22,6 +22,7 @@ from label_studio_sdk._legacy.exceptions import (
22
22
  LabelStudioValidationErrorSentryIgnored,
23
23
  )
24
24
 
25
+ from .base import LabelStudioTag
25
26
  from .control_tags import (
26
27
  ControlTag,
27
28
  ChoicesTag,
@@ -30,6 +31,7 @@ from .control_tags import (
30
31
  from .object_tags import ObjectTag
31
32
  from .label_tags import LabelTag
32
33
  from .objects import AnnotationValue, TaskValue, PredictionValue
34
+ from . import create as CE
33
35
 
34
36
 
35
37
  dir_path = os.path.dirname(os.path.realpath(__file__))
@@ -189,6 +191,67 @@ class LabelInterface:
189
191
  ```
190
192
  """
191
193
 
194
+ @classmethod
195
+ def create(cls, tags, mapping=None, title=None, style=None, pretty=True, *args, **kwargs):
196
+ """ Simple way of create UI config, it helps you not to thing much about the name/toName mapping
197
+
198
+ LabelInterface.create_simple({
199
+ "txt": "Text",
200
+ "chc": choices("positive", "negative")
201
+ })
202
+ """
203
+ tuples = CE.convert_tags_description(tags, mapping=mapping)
204
+
205
+ if isinstance(title, str):
206
+ tuples = (("Header", { "value": title }, {}),) + tuples
207
+
208
+ # in case we have either title or style, then we can iterate
209
+ # through the tuples and modify the tree
210
+ if isinstance(title, dict) or isinstance(style, dict):
211
+ new_tuples = []
212
+ for t in tuples:
213
+ tag, attributes, children = t
214
+ name = attributes.get("name", None)
215
+
216
+ # prepend Header tag to the list
217
+ if isinstance(title, dict) and name in title:
218
+ title_tag = ("Header", { "value": title.get(name) }, {})
219
+ new_tuples.append(title_tag)
220
+
221
+ # modify the style of the element by wrapping it into
222
+ # a View with style
223
+ if isinstance(style, dict) and name in style:
224
+ parent_tag = ("View", { "style": style.get(name) }, (t,))
225
+ new_tuples.append(parent_tag)
226
+ else:
227
+ new_tuples.append(t)
228
+
229
+ tuples = new_tuples
230
+
231
+ tree = CE.tree_from_tuples(*tuples)
232
+
233
+ return CE.tree_to_string(tree, pretty=pretty)
234
+
235
+
236
+ @classmethod
237
+ def create_instance(cls, *args, **kwargs):
238
+ """Create instance is a shortcut to create a config and then
239
+ parse it right away returning the LabelInterface object
240
+
241
+ ```
242
+ li = LabelInterface.create_instance({ "txt": "Text", "lbl": labels(("person", "org")) })
243
+ lbl = li.get_control("lbl")
244
+ reg = lbl.label("person", start=0, end=10)
245
+ ```
246
+
247
+ The above returns a region that could be serialized to Label
248
+ Studio JSON format and uploaded to Label Studio
249
+
250
+ """
251
+ config = cls.create(*args, **kwargs)
252
+ return cls(config=config, **kwargs)
253
+
254
+
192
255
  def __init__(self, config: str, tags_mapping=None, *args, **kwargs):
193
256
  """
194
257
  Initialize a LabelInterface instance using a config string.
@@ -240,6 +303,11 @@ class LabelInterface:
240
303
 
241
304
  ##### NEW API
242
305
 
306
+ @property
307
+ def config(self):
308
+ """Returns the XML configuration string"""
309
+ return self._config
310
+
243
311
  @property
244
312
  def controls(self):
245
313
  """Returns list of control tags"""
@@ -144,7 +144,14 @@ class ObjectTag(LabelStudioTag):
144
144
  """Check if value has variable"""
145
145
  pattern = re.compile(r"^\$[^, ]+$")
146
146
  return bool(pattern.fullmatch(self.value))
147
-
147
+
148
+ def collect_attrs(self):
149
+ """Return tag attrs as a single dict"""
150
+ return {
151
+ **self.attr,
152
+ "name": self.name,
153
+ "value": '$' + self.value if self.value is not None else None
154
+ }
148
155
 
149
156
  # and have generate_example in each
150
157
  def generate_example_value(self, mode="upload", secure_mode=False):
@@ -174,7 +181,8 @@ class ObjectTag(LabelStudioTag):
174
181
 
175
182
  class AudioTag(ObjectTag):
176
183
  """ """
177
-
184
+ tag: str = "Audio"
185
+
178
186
  def _generate_example(self, examples, only_urls=False):
179
187
  """ """
180
188
  return examples.get("Audio")
@@ -182,7 +190,8 @@ class AudioTag(ObjectTag):
182
190
 
183
191
  class ImageTag(ObjectTag):
184
192
  """ """
185
-
193
+ tag: str = "Image"
194
+
186
195
  def _generate_example(self, examples, only_urls=False):
187
196
  """ """
188
197
  return examples.get("Image")
@@ -190,7 +199,8 @@ class ImageTag(ObjectTag):
190
199
 
191
200
  class TableTag(ObjectTag):
192
201
  """ """
193
-
202
+ tag: str = "Table"
203
+
194
204
  def _generate_example(self, examples, only_urls=False):
195
205
  """ """
196
206
  return examples.get("Table")
@@ -198,7 +208,8 @@ class TableTag(ObjectTag):
198
208
 
199
209
  class TextTag(ObjectTag):
200
210
  """ """
201
-
211
+ tag: str = "Text"
212
+
202
213
  def _generate_example(self, examples, only_urls=False):
203
214
  """ """
204
215
  if only_urls:
@@ -209,7 +220,8 @@ class TextTag(ObjectTag):
209
220
 
210
221
  class VideoTag(ObjectTag):
211
222
  """ """
212
-
223
+ tag: str = "Video"
224
+
213
225
  def _generate_example(self, examples, only_urls=False):
214
226
  """ """
215
227
  return examples.get("Video")
@@ -217,7 +229,8 @@ class VideoTag(ObjectTag):
217
229
 
218
230
  class HyperTextTag(ObjectTag):
219
231
  """ """
220
-
232
+ tag: str = "HyperText"
233
+
221
234
  def _generate_example(self, examples, only_urls=False):
222
235
  """ """
223
236
  examples = data_examples(mode="upload")
@@ -229,7 +242,8 @@ class HyperTextTag(ObjectTag):
229
242
 
230
243
  class ListTag(ObjectTag):
231
244
  """ """
232
-
245
+ tag: str = "List"
246
+
233
247
  def _generate_example(self, examples, only_urls=False):
234
248
  """ """
235
249
  examples = data_examples(mode="upload")
@@ -238,7 +252,8 @@ class ListTag(ObjectTag):
238
252
 
239
253
  class ParagraphsTag(ObjectTag):
240
254
  """ """
241
-
255
+ tag: str = "Paragraphs"
256
+
242
257
  def _generate_example(self, examples, only_urls=False):
243
258
  """ """
244
259
  # Paragraphs special case - replace nameKey/textKey if presented
@@ -259,7 +274,8 @@ class ParagraphsTag(ObjectTag):
259
274
 
260
275
  class TimeSeriesTag(ObjectTag):
261
276
  """ """
262
-
277
+ tag: str = "TimeSeries"
278
+
263
279
  def _generate_example(self, examples, only_urls=False):
264
280
  """ """
265
281
  p = self.attr
@@ -20,13 +20,13 @@ def serialize_regions(result):
20
20
 
21
21
  class PredictionValue(BaseModel):
22
22
  """ """
23
-
24
23
  model_version: Optional[Any] = None
25
24
  score: Optional[float] = 0.00
26
25
  result: Optional[List[Union[Dict[str, Any], Region]]]
27
26
 
28
27
  class Config:
29
- allow_population_by_field_name = True
28
+ populate_by_name = True
29
+ protected_namespaces = ()
30
30
 
31
31
  @field_serializer('result')
32
32
  def serialize_result(self, result):
@@ -45,7 +45,7 @@ class AnnotationValue(BaseModel):
45
45
  result: Optional[List[Union[Dict[str, Any], Region]]]
46
46
 
47
47
  class Config:
48
- allow_population_by_field_name = True
48
+ populate_by_name = True
49
49
 
50
50
  @field_serializer('result')
51
51
  def serialize_result(self, result):
@@ -56,5 +56,5 @@ class TaskValue(BaseModel):
56
56
  """ """
57
57
 
58
58
  data: Optional[dict]
59
- annotations: Optional[List[AnnotationValue]]
60
- predictions: Optional[List[PredictionValue]]
59
+ annotations: Optional[List[AnnotationValue]] = Field(default_factory=list)
60
+ predictions: Optional[List[PredictionValue]] = Field(default_factory=list)