Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add some more Label Studio SDK unit tests #5149

Merged
merged 4 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions label_studio/tests/sdk/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# source: https://labelstud.io/guide/tasks.html#Basic-Label-Studio-JSON-format
LABEL_CONFIG_AND_TASKS = {
'label_config': """
<View>
<Text name="message" value="$my_text"/>
<Choices name="sentiment_class" toName="message">
<Choice value="Positive"/>
<Choice value="Neutral"/>
<Choice value="Negative"/>
</Choices>
</View>
""",
'tasks_for_import': [
{
'data': {
'my_text': 'Opossums are great',
'ref_id': 456,
'meta_info': {'timestamp': '2020-03-09 18:15:28.212882', 'location': 'North Pole'},
},
'annotations': [
{
'result': [
{
'from_name': 'sentiment_class',
'to_name': 'message',
'type': 'choices',
'readonly': False,
'hidden': False,
'value': {'choices': ['Positive']},
}
]
}
],
'predictions': [
{
'result': [
{
'from_name': 'sentiment_class',
'to_name': 'message',
'type': 'choices',
'readonly': False,
'hidden': False,
'value': {'choices': ['Neutral']},
}
],
'score': 0.95,
}
],
}
],
}
56 changes: 56 additions & 0 deletions label_studio/tests/sdk/test_annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import pytest

from label_studio.tests.sdk.common import LABEL_CONFIG_AND_TASKS

pytestmark = pytest.mark.django_db
from label_studio_sdk import Client


def test_annotation_create_and_update(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

task_data = [{'data': {'my_text': 'Test task'}}]
p.import_tasks(task_data)

tasks = p.get_tasks()
assert tasks[0]['data'] == task_data[0]['data']

task_id = tasks[0]['id']
annotation_data = {
'result': [{'from_name': 'label', 'to_name': 'my_text', 'type': 'choices', 'value': {'choices': ['Positive']}}]
}
new_annotation = p.create_annotation(task_id, **annotation_data)
assert (annotation_id := new_annotation['id'])
assert new_annotation['result'] == annotation_data['result']

p.update_annotation(
annotation_id,
result=[{'from_name': 'label', 'to_name': 'my_text', 'type': 'choices', 'value': {'choices': ['Negative']}}],
)

updated_annotation = p.get_tasks()[0]['annotations'][0]
assert updated_annotation['result'][0]['value'] == {'choices': ['Negative']}


def test_annotation_marks_task_as_labeled(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(
title='New Project',
label_config=LABEL_CONFIG_AND_TASKS['label_config'],
)

task_data = [{'data': {'my_text': 'Test task'}}, {'data': {'my_text': 'Test task 2'}}]
p.import_tasks(task_data)
tasks = p.get_tasks()

assert p.get_labeled_tasks() == []

task_id = tasks[0]['id']
annotation_data = {
'result': [{'from_name': 'label', 'to_name': 'my_text', 'type': 'choices', 'value': {'choices': ['Positive']}}]
}
p.create_annotation(task_id, **annotation_data)

assert len(labeled_tasks := p.get_labeled_tasks()) == 1
assert labeled_tasks[0]['data'] == task_data[0]['data']
82 changes: 10 additions & 72 deletions label_studio/tests/sdk/test_projects.py
Original file line number Diff line number Diff line change
@@ -1,87 +1,25 @@
import logging

import pytest

from label_studio.tests.sdk.common import LABEL_CONFIG_AND_TASKS

pytestmark = pytest.mark.django_db
from label_studio_sdk import Client
from tests.sdk.utils import sdk_logs


def test_upload_and_list_tasks_does_not_log_to_stderr(django_live_url, business_client, caplog):
caplog.set_level(logging.ERROR)

def test_start_and_get_project(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])
p.import_tasks(LABEL_CONFIG_AND_TASKS['tasks_for_import'])

tasks = p.get_tasks()

assert len(tasks) == 1
assert len(tasks[0]['annotations']) == 1
assert len(tasks[0]['predictions']) == 1
assert not sdk_logs(caplog)
project = ls.get_project(p.id)
assert project
assert project.title == 'New Project'


def test_get_empty_tasks_does_not_log_to_stderr(django_live_url, business_client, caplog):
caplog.set_level(logging.ERROR)

def test_delete_project(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

tasks = p.get_tasks()

assert not tasks
assert not sdk_logs(caplog)

project = ls.get_project(p.id)
ls.delete_project(project.id)

# source: https://labelstud.io/guide/tasks.html#Basic-Label-Studio-JSON-format
LABEL_CONFIG_AND_TASKS = {
'label_config': """
<View>
<Text name="message" value="$my_text"/>
<Choices name="sentiment_class" toName="message">
<Choice value="Positive"/>
<Choice value="Neutral"/>
<Choice value="Negative"/>
</Choices>
</View>
""",
'tasks_for_import': [
{
'data': {
'my_text': 'Opossums are great',
'ref_id': 456,
'meta_info': {'timestamp': '2020-03-09 18:15:28.212882', 'location': 'North Pole'},
},
'annotations': [
{
'result': [
{
'from_name': 'sentiment_class',
'to_name': 'message',
'type': 'choices',
'readonly': False,
'hidden': False,
'value': {'choices': ['Positive']},
}
]
}
],
'predictions': [
{
'result': [
{
'from_name': 'sentiment_class',
'to_name': 'message',
'type': 'choices',
'readonly': False,
'hidden': False,
'value': {'choices': ['Neutral']},
}
],
'score': 0.95,
}
],
}
],
}
assert ls.list_projects() == []
24 changes: 24 additions & 0 deletions label_studio/tests/sdk/test_storages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest

from label_studio.tests.sdk.common import LABEL_CONFIG_AND_TASKS

pytestmark = pytest.mark.django_db
from label_studio_sdk import Client


def test_connect_and_sync_s3(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

project = ls.get_project(p.id)
storage_resp = project.connect_s3_import_storage('pytest-s3-images')

storage_id = storage_resp['id']
ls.sync_storage('s3', storage_id)

assert set(t['storage_filename'] for t in p.get_tasks()) == {
'subdir/another/image2.jpg',
'subdir/image1.jpg',
'subdir/image2.jpg',
'image1.jpg',
}
98 changes: 98 additions & 0 deletions label_studio/tests/sdk/test_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import logging

import pytest

from label_studio.tests.sdk.common import LABEL_CONFIG_AND_TASKS

pytestmark = pytest.mark.django_db
from label_studio_sdk import Client
from tests.sdk.utils import sdk_logs


def test_task_CRUD(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

task_data = [{'data': {'my_text': 'Test task'}}]
p.import_tasks(task_data)

tasks = p.get_tasks()
assert len(tasks) == 1
assert (task_id := tasks[0]['id'])
assert tasks[0]['data'] == task_data[0]['data']

p.update_task(task_id, data={'my_text': 'Updated task'})
tasks = p.get_tasks()
assert len(tasks) == 1
assert tasks[0]['data'] == {'my_text': 'Updated task'}

p.delete_task(task_id)
assert not p.get_tasks()


def test_delete_multi_tasks(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

task_data = [{'data': {'my_text': 'Test task ' + str(i)}} for i in range(10)]
p.import_tasks(task_data)

tasks = p.get_tasks()
assert len(tasks) == 10

p.delete_tasks([t['id'] for t in tasks[:5]])
assert len(p.get_tasks()) == 5

p.delete_all_tasks(excluded_ids=[tasks[5]['id']])
remaining_tasks = p.get_tasks()
assert len(remaining_tasks) == 1
assert remaining_tasks[0]['data']['my_text'] == 'Test task 5'


def test_export_tasks(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

task_data = [{'data': {'my_text': 'Test task ' + str(i)}} for i in range(10)]
p.import_tasks(task_data)

task_id = p.get_tasks()[0]['id']
annotation_data = {
'result': [{'from_name': 'label', 'to_name': 'my_text', 'type': 'choices', 'value': {'choices': ['Positive']}}]
}
p.create_annotation(task_id, **annotation_data)

# by default, only tasks with annotations are exported
exported_tasks = p.export_tasks()
assert len(exported_tasks) == 1
assert exported_tasks[0]['data']['my_text'] == 'Test task 0'

exported_tasks = p.export_tasks(download_all_tasks=True)
assert len(exported_tasks) == 10


def test_upload_and_list_tasks_does_not_log_to_stderr(django_live_url, business_client, caplog):
caplog.set_level(logging.ERROR)

ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])
p.import_tasks(LABEL_CONFIG_AND_TASKS['tasks_for_import'])

tasks = p.get_tasks()

assert len(tasks) == 1
assert len(tasks[0]['annotations']) == 1
assert len(tasks[0]['predictions']) == 1
assert not sdk_logs(caplog)


def test_get_empty_tasks_does_not_log_to_stderr(django_live_url, business_client, caplog):
caplog.set_level(logging.ERROR)

ls = Client(url=django_live_url, api_key=business_client.api_key)
p = ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

tasks = p.get_tasks()

assert not tasks
assert not sdk_logs(caplog)
24 changes: 24 additions & 0 deletions label_studio/tests/sdk/test_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest

from label_studio.tests.sdk.common import LABEL_CONFIG_AND_TASKS

pytestmark = pytest.mark.django_db

from label_studio_sdk import Client


def test_add_user(django_live_url, business_client):
ls = Client(url=django_live_url, api_key=business_client.api_key)
ls.start_project(title='New Project', label_config=LABEL_CONFIG_AND_TASKS['label_config'])

test_member_email = '[email protected]'
u = ls.create_user(
{
'email': test_member_email,
'username': test_member_email,
'first_name': 'Test',
'last_name': 'Member',
}
)

assert u.id in [u.id for u in ls.get_users()]
Loading
Loading