label-studio-sdk 0.0.30__py3-none-any.whl → 0.0.34__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 (38) hide show
  1. label_studio_sdk/__init__.py +4 -1
  2. label_studio_sdk/client.py +104 -85
  3. label_studio_sdk/data_manager.py +32 -23
  4. label_studio_sdk/exceptions.py +10 -0
  5. label_studio_sdk/label_interface/__init__.py +1 -0
  6. label_studio_sdk/label_interface/base.py +77 -0
  7. label_studio_sdk/label_interface/control_tags.py +756 -0
  8. label_studio_sdk/label_interface/interface.py +922 -0
  9. label_studio_sdk/label_interface/label_tags.py +72 -0
  10. label_studio_sdk/label_interface/object_tags.py +292 -0
  11. label_studio_sdk/label_interface/region.py +43 -0
  12. label_studio_sdk/objects.py +35 -0
  13. label_studio_sdk/project.py +725 -262
  14. label_studio_sdk/schema/label_config_schema.json +226 -0
  15. label_studio_sdk/users.py +15 -13
  16. label_studio_sdk/utils.py +31 -30
  17. label_studio_sdk/workspaces.py +13 -11
  18. {label_studio_sdk-0.0.30.dist-info → label_studio_sdk-0.0.34.dist-info}/METADATA +7 -5
  19. label_studio_sdk-0.0.34.dist-info/RECORD +37 -0
  20. {label_studio_sdk-0.0.30.dist-info → label_studio_sdk-0.0.34.dist-info}/WHEEL +1 -1
  21. {label_studio_sdk-0.0.30.dist-info → label_studio_sdk-0.0.34.dist-info}/top_level.txt +0 -1
  22. tests/test_client.py +21 -10
  23. tests/test_export.py +105 -0
  24. tests/test_interface/__init__.py +1 -0
  25. tests/test_interface/configs.py +137 -0
  26. tests/test_interface/mockups.py +22 -0
  27. tests/test_interface/test_compat.py +64 -0
  28. tests/test_interface/test_control_tags.py +55 -0
  29. tests/test_interface/test_data_generation.py +45 -0
  30. tests/test_interface/test_lpi.py +15 -0
  31. tests/test_interface/test_main.py +196 -0
  32. tests/test_interface/test_object_tags.py +36 -0
  33. tests/test_interface/test_region.py +36 -0
  34. tests/test_interface/test_validate_summary.py +35 -0
  35. tests/test_interface/test_validation.py +59 -0
  36. docs/__init__.py +0 -3
  37. label_studio_sdk-0.0.30.dist-info/RECORD +0 -15
  38. {label_studio_sdk-0.0.30.dist-info → label_studio_sdk-0.0.34.dist-info}/LICENSE +0 -0
tests/test_export.py ADDED
@@ -0,0 +1,105 @@
1
+ from unittest.mock import Mock
2
+
3
+ import requests_mock
4
+
5
+ from label_studio_sdk.client import Client
6
+ from label_studio_sdk.data_manager import Filters, Operator, Type, Column
7
+
8
+
9
+ def test_client_headers():
10
+ mock_session = Mock(spec=["request"])
11
+ mock_session.request.return_value = Mock(status_code=200)
12
+ client = Client(
13
+ url="http://fake.url",
14
+ api_key="fake_key",
15
+ session=mock_session,
16
+ versions={"label-studio": "1.0.0"},
17
+ extra_headers={"Proxy-Authorization": "Bearer fake_bearer"},
18
+ )
19
+
20
+ client.check_connection()
21
+ args, kwargs = mock_session.request.call_args
22
+ assert kwargs["headers"] == {
23
+ "Authorization": f"Token fake_key",
24
+ "Proxy-Authorization": "Bearer fake_bearer",
25
+ }
26
+
27
+
28
+ def test_client_no_extra_headers():
29
+ mock_session = Mock(spec=["request"])
30
+ mock_session.request.return_value = Mock(status_code=200)
31
+ client = Client(
32
+ url="http://fake.url",
33
+ api_key="fake_key",
34
+ session=mock_session,
35
+ versions={"label-studio": "1.0.0"},
36
+ )
37
+
38
+ client.check_connection()
39
+ args, kwargs = mock_session.request.call_args
40
+ assert kwargs["headers"] == {"Authorization": f"Token fake_key"}
41
+
42
+
43
+ def test_project_export_with_filters():
44
+ with requests_mock.Mocker() as m:
45
+ m.get(
46
+ 'http://fake.url/api/version',
47
+ json={"version": "1.0.0"},
48
+ status_code=200,
49
+ )
50
+ m.get(
51
+ 'http://fake.url/api/projects/1',
52
+ json={"id": 1, "title": "fake_project"},
53
+ status_code=200,
54
+ )
55
+ m.post(
56
+ 'http://fake.url/api/projects/1/exports',
57
+ json={"id": 1, "title": "fake_project"},
58
+ status_code=200,
59
+ )
60
+ m.post(
61
+ 'http://fake.url/api/dm/views',
62
+ json={"id": 1, "title": "fake_project"},
63
+ status_code=200,
64
+ )
65
+ m.post(
66
+ 'http://fake.url/api/projects/1/exports',
67
+ json={"id": 1, "title": "fake_project"},
68
+ status_code=200,
69
+ )
70
+ m.get(
71
+ 'http://fake.url/api/projects/1/exports/1',
72
+ json={"id": 1, "title": "fake_project", "status": "completed"},
73
+ status_code=200,
74
+ )
75
+ m.get(
76
+ 'http://fake.url/api/projects/1/exports/1/download?exportType=JSON',
77
+ headers={"Content-Disposition": "attachment; filename=fake_project.json"},
78
+ status_code=200,
79
+ )
80
+ m.delete(
81
+ 'http://fake.url/api/dm/views/1',
82
+ status_code=200,
83
+ )
84
+
85
+ filters = Filters.create(
86
+ Filters.AND,
87
+ [
88
+ Filters.item(
89
+ Column.inner_id,
90
+ Operator.GREATER_OR_EQUAL,
91
+ Type.Number,
92
+ Filters.value(1),
93
+ ),
94
+ Filters.item(
95
+ Column.inner_id,
96
+ Operator.LESS,
97
+ Type.Number,
98
+ Filters.value(100),
99
+ ),
100
+ ],
101
+ )
102
+
103
+ ls = Client(url='http://fake.url', api_key='fake_key')
104
+ project = ls.get_project(1)
105
+ project.export(filters=filters)
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,137 @@
1
+ FROM_NAME = "from_name"
2
+ FROM_NAME_PREFIX = "from"
3
+ TO_NAME = "to_name"
4
+ ANOTHER_NAME = "another_name"
5
+ ANOTHER_TO_NAME = "another_to_name"
6
+
7
+ VALUE = "$var"
8
+ VALUE_KEY = "var"
9
+ ANOTHER_VALUE = "$var2"
10
+
11
+ LABEL1 = "yes"
12
+ LABEL2 = "no"
13
+
14
+ CORRECT_TASK = {VALUE_KEY: "value"}
15
+
16
+ CORRECT_REGION = {
17
+ "from_name": FROM_NAME,
18
+ "to_name": TO_NAME,
19
+ "type": "choices",
20
+ "value": {"choices": [LABEL1]},
21
+ }
22
+
23
+ SIMPLE_CONF = f"""
24
+ <View>
25
+ <Text name="{TO_NAME}" value="{VALUE}" />
26
+ <Choices name="{FROM_NAME}" toName="{TO_NAME}">
27
+ <Choice value="{LABEL1}" />
28
+ <Choice value="{LABEL2}" />
29
+ </Choices>
30
+ </View>
31
+ """
32
+
33
+ SIMPLE_WRONG_CONF = f"""
34
+ <View>
35
+ <Text name="{TO_NAME}" value="{VALUE}" />
36
+ <Text name="{TO_NAME}" value="{VALUE}" />
37
+ <Choices name="{FROM_NAME}" toName="{ANOTHER_TO_NAME}">
38
+ <Choice value="{LABEL1}" />
39
+ <Choice value="{LABEL2}" />
40
+ </Choices>
41
+ </View>
42
+ """
43
+
44
+ CONF_WITH_COMMENT = (
45
+ '<!-- {"data": { "hello": "world" }, "predictions": [], "annotations": [] } -->'
46
+ + f"""{SIMPLE_CONF}"""
47
+ )
48
+
49
+ CONF_COMPLEX = f"""
50
+ <View>
51
+ <Labels name="label" toName="text">
52
+ <Label value="PER" background="red"/>
53
+ <Label value="ORG" background="darkorange"/>
54
+ <Label value="LOC" background="orange"/>
55
+ <Label value="MISC" background="green"/>
56
+ </Labels>
57
+
58
+ <Text name="text" value="$text"/>
59
+ <Choices name="sentiment" toName="text"
60
+ choice="single" showInLine="true">
61
+ <Choice value="Positive"/>
62
+ <Choice value="Negative"/>
63
+ <Choice value="Neutral"/>
64
+ </Choices>
65
+ </View>
66
+ """
67
+
68
+ TWO_TONAMES = f"""
69
+ <View>
70
+ <Text name="{TO_NAME}" value="{VALUE}" />
71
+ <Text name="{ANOTHER_TO_NAME}" value="{VALUE}" />
72
+ <Choices name="{FROM_NAME}" toName="{TO_NAME},{ANOTHER_TO_NAME}">
73
+ <Choice value="{LABEL1}" />
74
+ <Choice value="{LABEL2}" />
75
+ </Choices>
76
+ </View>
77
+ """
78
+
79
+ TEXTAREA_CONF = f"""
80
+ <View>
81
+ <Textarea name="{FROM_NAME}" toName="{TO_NAME}" />
82
+ <Text name="{TO_NAME}" value="{VALUE}" />
83
+
84
+ <Choices name="{ANOTHER_NAME}" toName="{TO_NAME}">
85
+ <Choice value="{LABEL1}" />
86
+ <Choice value="{LABEL2}" />
87
+ </Choices>
88
+ </View>
89
+ """
90
+
91
+ VIDEO_CONF = f"""
92
+ <View>
93
+ <Labels name="videoLabels" toName="video">
94
+ <Label value="Car"/>
95
+ <Label value="Person"/>
96
+ </Labels>
97
+ <Video name="video" value="$video"/>
98
+ <VideoRectangle name="box" toName="video"/>
99
+ </View>
100
+ """
101
+
102
+ NO_DYNAMIC_LABELS_CONF = f"""
103
+ <View>
104
+ <Header value="Select label and click the image to start"/>
105
+ <Image name="image" value="$image" zoom="true"/>
106
+ <PolygonLabels name="label" toName="image"
107
+ strokeWidth="3" pointSize="small"
108
+ opacity="0.9" />
109
+ </View>
110
+ """
111
+
112
+ DYNAMIC_LABELS_CONF = f"""
113
+ <View>
114
+ <Header value="Select label and click the image to start"/>
115
+ <Image name="image" value="$image" zoom="true"/>
116
+ <PolygonLabels name="label" toName="image"
117
+ strokeWidth="3" pointSize="small"
118
+ opacity="0.9" value="$options" />
119
+ </View>
120
+ """
121
+
122
+ RECT_CONFIG = f"""
123
+ <View>
124
+ <Image name="{TO_NAME}" value="{VALUE}" />
125
+ <Rectangle name="{FROM_NAME}" toName="{TO_NAME}"></Rectangle>
126
+ </View>
127
+ """
128
+
129
+ EMPTY_VALUE_CONF = f"""
130
+ <View>
131
+ <Text name="text" value="$text"/>
132
+ <Labels name="type" toName="text">
133
+ <Label value="" />
134
+ <Label value="" />
135
+ </Labels>
136
+ </View>
137
+ """
@@ -0,0 +1,22 @@
1
+ import json
2
+
3
+ # Example of the output of summary table for a specific project
4
+ # project_id created_at all_data_columns common_data_columns created_annotations created_labels created_labels_drafts
5
+ # ---------- -------------------------- ----------------------------- --------------------- ----------------------------------------------------- ------------------------------------------------------------ ----------------------------------------------------
6
+ # 9 2024-03-02 20:47:43.710926 {"text": 20, "sentiment": 20} ["sentiment", "text"] {"label|text|labels": 2, "sentiment|text|choices": 2} {"label": {"PER": 1, "ORG": 1}, "sentiment": {"Positive": 1, {"label": {"MISC": 1}, "sentiment": {"Negative": 1}}
7
+ # "Negative": 1}}
8
+
9
+
10
+ class SummaryMockup:
11
+ """
12
+ """
13
+
14
+ created_labels = json.loads(
15
+ '{"label": {"PER": 1, "ORG": 1}, "sentiment": {"Positive": 1, "Negative": 1}}'
16
+ )
17
+ created_labels_drafts = json.loads(
18
+ '{"label": {"MISC": 1}, "sentiment": {"Negative": 1}}'
19
+ )
20
+ created_annotations = json.loads(
21
+ '{"label|text|labels": 2, "sentiment|text|choices": 2}'
22
+ )
@@ -0,0 +1,64 @@
1
+ ## Testing compatibility functions
2
+ ##
3
+ ## Compatibility function is a function that was implemented in
4
+ ## label_studio.core.label_config or tools and is reimplemented within
5
+ ## SDK using new LabelInterface
6
+
7
+ import pytest
8
+
9
+ from label_studio_sdk.label_interface import LabelInterface
10
+ from label_studio_sdk.objects import PredictionValue
11
+
12
+ from . import configs as c
13
+
14
+
15
+ def test_get_first_tag_occurence_simple():
16
+ conf = LabelInterface(c.SIMPLE_CONF)
17
+ from_name, to_name, value = conf.get_first_tag_occurence("Choices", "Text")
18
+
19
+ assert from_name == c.FROM_NAME
20
+ assert to_name == c.TO_NAME
21
+ assert value == c.VALUE_KEY
22
+
23
+ with pytest.raises(ValueError):
24
+ conf.get_first_tag_occurence("Choices", "Image")
25
+
26
+ with pytest.raises(ValueError):
27
+ conf.get_first_tag_occurence("Labels", "Text")
28
+
29
+ with pytest.raises(ValueError):
30
+ conf.get_first_tag_occurence("Labels", "Image")
31
+
32
+
33
+ def test_get_first_tag_occurence_complex():
34
+ conf = LabelInterface(c.SIMPLE_CONF)
35
+ from_name, to_name, value = conf.get_first_tag_occurence(
36
+ "Choices",
37
+ ("Image", "Text"),
38
+ name_filter=lambda s: s.startswith(c.FROM_NAME_PREFIX),
39
+ to_name_filter=lambda s: s == c.TO_NAME,
40
+ )
41
+
42
+ assert from_name == c.FROM_NAME
43
+ assert to_name == c.TO_NAME
44
+ assert value == c.VALUE_KEY
45
+
46
+ with pytest.raises(ValueError):
47
+ conf.get_first_tag_occurence(
48
+ "Choices",
49
+ ("Image", "Text"),
50
+ name_filter=lambda s: s.startswith("wrong_prefix"),
51
+ )
52
+
53
+ with pytest.raises(ValueError):
54
+ conf.get_first_tag_occurence(
55
+ "Choices", ("Image", "Text"), to_name_filter=lambda s: s == "wrong_name"
56
+ )
57
+
58
+
59
+ def test_is_video_object_tracking():
60
+ conf1 = LabelInterface(c.SIMPLE_CONF)
61
+ assert conf1.is_video_object_tracking() is False
62
+
63
+ conf2 = LabelInterface(c.VIDEO_CONF)
64
+ assert conf2.is_video_object_tracking() is True
@@ -0,0 +1,55 @@
1
+ import json
2
+ from lxml.etree import Element
3
+
4
+ from label_studio_sdk.label_interface import LabelInterface
5
+ from label_studio_sdk.label_interface.control_tags import ControlTag
6
+
7
+ import tests.test_interface.configs as c
8
+
9
+
10
+ def test_parse():
11
+ tag = Element(
12
+ "tag",
13
+ {"name": "my_name", "toName": "name1,name2", "apiUrl": "http://myapi.com"},
14
+ )
15
+ control_tag = ControlTag.parse_node(tag)
16
+
17
+ assert isinstance(control_tag, ControlTag)
18
+ assert control_tag.tag == "tag"
19
+ assert control_tag.name == "my_name"
20
+ assert control_tag.to_name == ["name1", "name2"]
21
+ assert control_tag.dynamic_value == True
22
+
23
+
24
+ def test_validate():
25
+ tag = Element("tag", {"name": "my_name", "toName": "name1,name2"})
26
+ is_control_tag1 = ControlTag.validate_node(tag)
27
+
28
+ assert is_control_tag1 == True
29
+
30
+ tag2 = Element("not_control_tag", {"name": "my_name"})
31
+ is_control_tag2 = ControlTag.validate_node(tag2)
32
+
33
+ assert is_control_tag2 == False
34
+
35
+
36
+ def test_textarea_label():
37
+ conf = LabelInterface(c.TEXTAREA_CONF)
38
+
39
+ region = conf.get_control(c.FROM_NAME).label(text=["Hello", "World"])
40
+
41
+
42
+ def test_label_with_choices():
43
+ conf = LabelInterface(c.SIMPLE_CONF)
44
+ region = conf.get_control().label(label=c.LABEL1)
45
+
46
+ rjs = region.to_json()
47
+ assert isinstance(rjs, str)
48
+
49
+ rpy = json.loads(rjs)
50
+ assert rpy["from_name"] == c.FROM_NAME
51
+ assert rpy["to_name"] == c.TO_NAME
52
+ assert "value" in rpy
53
+
54
+ assert "choices" in rpy.get("value")
55
+ assert c.LABEL1 in rpy["value"]["choices"]
@@ -0,0 +1,45 @@
1
+ from lxml.etree import Element
2
+
3
+ from label_studio_sdk.label_interface import LabelInterface
4
+ from label_studio_sdk.label_interface.object_tags import ObjectTag
5
+ import tests.test_interface.configs as c
6
+
7
+
8
+ def test_generate_sample_task():
9
+ conf = LabelInterface(c.SIMPLE_CONF)
10
+ task = conf.generate_sample_task()
11
+ value = c.VALUE[1:]
12
+
13
+ print(task)
14
+
15
+ assert value in task
16
+ assert len(task[value])
17
+
18
+
19
+ def test_generate_url():
20
+ """Quick check that each object tag generates the right data
21
+ """
22
+
23
+ def url_validator(url):
24
+ assert url.startswith("https://") or url.startswith("http://")
25
+
26
+ # TODO need to add other validators
27
+ m = {
28
+ "Audio": url_validator,
29
+ "Image": url_validator,
30
+ # "Table": None,
31
+ "Text": url_validator,
32
+ "Video": url_validator,
33
+ # "HyperText": None,
34
+ # "List": None,
35
+ "Paragraphs": url_validator,
36
+ # "TimeSeries": url_validator
37
+ }
38
+
39
+ for tag_name, validator in m.items():
40
+
41
+ tag = Element(tag_name, {"name": "my_name", "value": "my_value"})
42
+ inst = ObjectTag.parse_node(tag)
43
+
44
+ res = inst.generate_example_value(mode="editor_preview", secure_mode=True)
45
+ validator(res)
@@ -0,0 +1,15 @@
1
+ from label_studio_sdk.label_interface.region import Region
2
+ from label_studio_sdk.label_interface.object_tags import ImageTag
3
+ from label_studio_sdk.label_interface.control_tags import RectangleTag
4
+
5
+
6
+ def test_li():
7
+ """Test using Label Interface to label things
8
+ """
9
+ img = ImageTag(
10
+ name="img", tag="image", value="http://example.com/image.jpg", attr={}
11
+ )
12
+ rect = RectangleTag(name="rect", to_name=["img"], tag="rectangle", attr={})
13
+ rect.set_object(img)
14
+
15
+ region = rect.label(x=10, y=10, width=10, height=10, rotation=10)
@@ -0,0 +1,196 @@
1
+ """
2
+ """
3
+ import json
4
+ import pytest
5
+ import xmljson
6
+ import copy
7
+
8
+ from label_studio_sdk.objects import PredictionValue
9
+ from label_studio_sdk.label_interface import LabelInterface
10
+ from label_studio_sdk.label_interface.control_tags import (
11
+ ControlTag,
12
+ ChoicesTag,
13
+ LabelsTag,
14
+ Region,
15
+ )
16
+ from label_studio_sdk.exceptions import LabelStudioValidationErrorSentryIgnored
17
+
18
+ # from label_studio_sdk.label_config.regions import Region
19
+ import tests.test_interface.configs as c
20
+
21
+
22
+ ## testing basic functionality
23
+
24
+
25
+ def test_parse_configs():
26
+ conf1 = LabelInterface(c.SIMPLE_CONF)
27
+ conf2 = LabelInterface(c.VIDEO_CONF)
28
+ conf3 = LabelInterface(c.DYNAMIC_LABELS_CONF)
29
+
30
+
31
+ def test_accessors():
32
+ conf = LabelInterface(c.SIMPLE_CONF)
33
+
34
+ c1 = conf.get_control()
35
+ assert c1.name == c.FROM_NAME
36
+
37
+ c2 = conf.get_control(c.FROM_NAME)
38
+ assert c2.name == c.FROM_NAME
39
+
40
+ o1 = conf.get_object(c.TO_NAME)
41
+ assert o1.name == c.TO_NAME
42
+
43
+ o2 = c2.get_object(c.TO_NAME)
44
+ assert o2.name == c.TO_NAME
45
+
46
+
47
+ def test_parse_two_to_names():
48
+ conf = LabelInterface(c.TWO_TONAMES)
49
+ ctrl = conf.get_control()
50
+
51
+ assert isinstance(ctrl, ControlTag)
52
+
53
+ with pytest.raises(Exception):
54
+ obj = conf.get_object()
55
+
56
+ obj1 = conf.get_object(c.TO_NAME)
57
+ assert obj1.name == c.TO_NAME
58
+
59
+ obj2 = conf.get_object(c.ANOTHER_TO_NAME)
60
+ assert obj2.name == c.ANOTHER_TO_NAME
61
+
62
+
63
+ def test_parse_textarea():
64
+ conf = LabelInterface(c.TEXTAREA_CONF)
65
+
66
+
67
+ # def test_parse_config_to_json():
68
+ # json = LabelInterface.parse_config_to_json(c.SIMPLE_CONF)
69
+
70
+
71
+ def test_to_name_validation():
72
+ LabelInterface._to_name_validation(None, c.SIMPLE_CONF)
73
+
74
+ with pytest.raises(LabelStudioValidationErrorSentryIgnored):
75
+ LabelInterface._to_name_validation(None, c.SIMPLE_WRONG_CONF)
76
+
77
+
78
+ def test_unique_names_validation():
79
+ with pytest.raises(LabelStudioValidationErrorSentryIgnored):
80
+ LabelInterface._unique_names_validation(None, c.SIMPLE_WRONG_CONF)
81
+
82
+
83
+ def test_get_sample_task():
84
+ conf = LabelInterface(c.SIMPLE_CONF)
85
+ task, _, _ = conf._sample_task()
86
+ value = c.VALUE[1:]
87
+
88
+ assert value in task
89
+ assert len(task[value])
90
+
91
+
92
+ ## various edge cases
93
+
94
+
95
+ def test_config_essential_data_has_changed():
96
+ conf = LabelInterface(c.SIMPLE_CONF)
97
+ assert conf.config_essential_data_has_changed(c.SIMPLE_CONF) is False
98
+
99
+ new_conf = c.SIMPLE_CONF.replace(c.FROM_NAME, "wrong_name")
100
+ assert conf.config_essential_data_has_changed(new_conf) is True
101
+
102
+ new_conf_2 = c.SIMPLE_CONF.replace(c.LABEL1, "wrong_label")
103
+ assert conf.config_essential_data_has_changed(new_conf_2) is True
104
+
105
+
106
+ def test_get_task_from_labeling_config():
107
+ task_data, annotations, predictions = LabelInterface.get_task_from_labeling_config(
108
+ c.CONF_WITH_COMMENT
109
+ )
110
+
111
+ # assert task_data == "some_data"
112
+ # assert annotations == "some_annotations"
113
+ # assert predictions == "some_predictions"
114
+
115
+
116
+ def test_find_tags():
117
+ conf = LabelInterface(c.SIMPLE_CONF)
118
+ tags = conf.find_tags(match_fn=lambda tag: tag.name == c.TO_NAME)
119
+
120
+ assert len(tags) > 0
121
+ assert tags[0].name == c.TO_NAME
122
+
123
+
124
+ def test_find_tags_by_class():
125
+ conf = LabelInterface(c.SIMPLE_CONF)
126
+ tags = conf.find_tags_by_class(ChoicesTag)
127
+
128
+ assert len(tags) > 0
129
+ assert tags[0].name == c.FROM_NAME
130
+
131
+ tags2 = conf.find_tags_by_class(LabelsTag)
132
+ assert len(tags2) == 0
133
+
134
+
135
+ ## testing generation
136
+
137
+
138
+ def test_task_generation():
139
+ val = c.VALUE[1:]
140
+ conf = LabelInterface(c.SIMPLE_CONF)
141
+ task = conf.generate_sample_task()
142
+
143
+ assert val in task
144
+ print(task)
145
+ assert len(task.get(val))
146
+
147
+
148
+ ## testing object tags
149
+
150
+ ## testing control tags
151
+
152
+
153
+ def test_label_with_choices():
154
+ conf = LabelInterface(c.SIMPLE_CONF)
155
+ region: Region = conf.get_control().label(label=c.LABEL1)
156
+
157
+ rjs = region.to_json()
158
+ assert isinstance(rjs, str)
159
+
160
+ rpy = json.loads(rjs)
161
+ assert rpy["from_name"] == c.FROM_NAME
162
+ assert rpy["to_name"] == c.TO_NAME
163
+ assert "value" in rpy
164
+
165
+ print(rpy)
166
+
167
+ assert "choices" in rpy.get("value")
168
+ assert c.LABEL1 in rpy["value"]["choices"]
169
+
170
+
171
+ ## testing all other tags
172
+
173
+ ## test other method
174
+
175
+
176
+ def test_load_task():
177
+ conf = LabelInterface(c.SIMPLE_CONF)
178
+ var_name = c.VALUE[1:]
179
+ value = "test"
180
+
181
+ tree = conf.load_task({var_name: value})
182
+
183
+ assert isinstance(tree, LabelInterface)
184
+ assert tree.get_object(c.TO_NAME).value == value
185
+
186
+
187
+ def test_load_random_task():
188
+ conf = LabelInterface(c.SIMPLE_CONF)
189
+ task, _, _ = conf._sample_task()
190
+
191
+ tree = conf.load_task(task)
192
+ assert len(tree.get_object(c.TO_NAME).value)
193
+
194
+
195
+ def test_empty_value_config():
196
+ conf = LabelInterface(c.EMPTY_VALUE_CONF)
@@ -0,0 +1,36 @@
1
+ from label_studio_sdk.label_interface.object_tags import ObjectTag
2
+ from lxml.etree import Element
3
+
4
+
5
+ def test_parse():
6
+ tag = Element("tag", {"name": "my_name", "value": "my_value"})
7
+ object_tag = ObjectTag.parse_node(tag)
8
+
9
+ assert object_tag.name == "my_name"
10
+ assert object_tag.value == "my_value"
11
+ assert object_tag.value_type == None
12
+
13
+
14
+ def test_validate():
15
+ tag = Element("tag", {"name": "my_name", "value": "$my_value"})
16
+ validation_result = ObjectTag.validate_node(tag)
17
+
18
+ assert validation_result == True
19
+
20
+
21
+ def test_value_type():
22
+ tag = Element(
23
+ "tag", {"name": "my_name", "value": "my_value", "valueType": "string"}
24
+ )
25
+ object_tag = ObjectTag.parse_node(tag)
26
+ tag_value_type = object_tag.value_type
27
+
28
+ assert tag_value_type == "string"
29
+
30
+
31
+ def test_value_is_variable():
32
+ tag = Element("tag", {"name": "my_name", "value": "$my_var"})
33
+ object_tag = ObjectTag.parse_node(tag)
34
+ is_variable = object_tag.value_is_variable
35
+
36
+ assert is_variable == True