diff --git a/.fernignore b/.fernignore index 3802be0..65bb519 100644 --- a/.fernignore +++ b/.fernignore @@ -33,23 +33,23 @@ tests/test_webhooks.py # manual workflows .github/workflows/ci.yml -.github/release.yml -.github/pr-title-checker-config.json -.github/autolabeler.yml -.github/CODEOWNERS -.github/workflows/slash-command-dispatch.yml -.github/workflows/follow-merge-sync-pr-lse.yml -.github/workflows/lint.yml .github/workflows/build_pypi.yml .github/workflows/codeql.yml -.github/workflows/release-set-version.yml -.github/workflows/release-pipeline.yml .github/workflows/docs.yml -.github/workflows/release-cut-off-release-branch.yml -.github/workflows/jira-command.yml -.github/workflows/tests.yml -.github/workflows/pr-labeler.yml +.github/workflows/follow-merge-sync-pr-adala.yml .github/workflows/follow-merge-sync-pr-lso.yml .github/workflows/follow-merge-upstream-repo-sync.yml +.github/workflows/jira-command.yml +.github/workflows/lint.yml +.github/workflows/pr-labeler.yml +.github/workflows/release-cut-off-release-branch.yml +.github/workflows/release-pipeline.yml +.github/workflows/release-set-version.yml +.github/workflows/slash-command-dispatch.yml +.github/workflows/tests.yml .github/workflows/update-draft-release.yml +.github/pr-title-checker-config.json +.github/release.yml +.github/autolabeler.yml +.github/CODEOWNERS .github/dependabot.yml diff --git a/.github/workflows/follow-merge-upstream-repo-sync.yml b/.github/workflows/follow-merge-upstream-repo-sync.yml index 0052798..6219af9 100644 --- a/.github/workflows/follow-merge-upstream-repo-sync.yml +++ b/.github/workflows/follow-merge-upstream-repo-sync.yml @@ -77,7 +77,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.12' - cache: 'poetry' +# cache: 'poetry' # Disable cache since label-studio-client-generator does not have poetry.lock - name: Download Fern run: npm install -g fern-api diff --git a/src/label_studio_sdk/label_interface/control_tags.py b/src/label_studio_sdk/label_interface/control_tags.py index 442d213..45e0213 100644 --- a/src/label_studio_sdk/label_interface/control_tags.py +++ b/src/label_studio_sdk/label_interface/control_tags.py @@ -462,6 +462,23 @@ def label( ) else: return self._label_simple(to_name=to_name, *args, **kwargs) + + def get_labels(self, regions: List[Dict]): + """ + Returns the simplified representation of the label. Sort of a reverse to label() method to retrieve an input `label` from an output regions + """ + values = [region.get('value') for region in regions if region.get('from_name') == self.name] + values = list(filter(lambda x: x is not None, values)) + if not hasattr(self, "_label_attr_name"): + return values + labels = [] + for value in values: + if len(value) == 1 and self._label_attr_name in value: + v = value[self._label_attr_name] + labels.append(v[0] if len(v) == 1 else v) + else: + labels.append(value) + return labels[0] if len(labels) == 1 else labels def as_tuple(self): """ """ diff --git a/tests/custom/test_interface/test_control_tags.py b/tests/custom/test_interface/test_control_tags.py index 73910bd..5ea724c 100644 --- a/tests/custom/test_interface/test_control_tags.py +++ b/tests/custom/test_interface/test_control_tags.py @@ -1,8 +1,9 @@ import json +import pytest from lxml.etree import Element from label_studio_sdk.label_interface import LabelInterface -from label_studio_sdk.label_interface.control_tags import ControlTag +from label_studio_sdk.label_interface.control_tags import ControlTag, ChoicesTag, LabelsTag, RectangleLabelsTag from . import configs as c @@ -53,3 +54,53 @@ def test_label_with_choices(): assert "choices" in rpy.get("value") assert c.LABEL1 in rpy["value"]["choices"] + + +@pytest.mark.parametrize("tag, regions, expected", [ + # Test case 1: ChoicesTag with single choice + ( + ChoicesTag(name="choices", to_name=["text"], tag="Choices"), + [{"from_name": "choices", "value": {"choices": ["positive"]}}], + "positive" + ), + + # Test case 2: ChoicesTag with multiple choices + ( + ChoicesTag(name="choices", to_name=["text"], tag="Choices"), + [{"from_name": "choices", "value": {"choices": ["positive", "negative"]}}], + ["positive", "negative"] + ), + + # Test case 3: Multiple regions with labels + ( + LabelsTag(name="label", to_name=["text"], tag="Labels"), + [ + {"from_name": "label", "to_name": "text", "value": {"labels": ["positive"], "start": 0, "end": 1}}, + {"from_name": "label", "to_name": "text", "value": {"labels": ["negative"], "start": 2, "end": 3}} + ], + [{"start": 0, "end": 1, "labels": ["positive"]}, {"start": 2, "end": 3, "labels": ["negative"]}] + ), + + # Test case 4: Empty regions + ( + ChoicesTag(name="choices", to_name=["text"], tag="Choices"), + [], + [] + ), + + # Test case 5: Regions with different from_name + ( + ChoicesTag(name="choices", to_name=["text"], tag="Choices"), + [{"from_name": "other_tag", "value": {"choices": ["positive"]}}], + [] + ), + + # Test case 6: Tag without label_attr_name + ( + ControlTag(name="base", to_name=["text"], tag="BaseTag"), + [{"from_name": "base", "value": {"some_value": 42}}], + [{"some_value": 42}] + ), +]) +def test_control_tag_get_labels(tag, regions, expected): + assert tag.get_labels(regions) == expected