From 2b10583e3e205bab02e1a67b1f9724158f28b6fa Mon Sep 17 00:00:00 2001 From: hakan458 Date: Tue, 23 Sep 2025 12:17:13 -0700 Subject: [PATCH 01/21] fix: UTC-299: Dont use org members for agreement selected annotators --- label_studio/projects/api.py | 43 ++++++++++++++++++++++++++++++++++- label_studio/projects/urls.py | 2 ++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/label_studio/projects/api.py b/label_studio/projects/api.py index 1b9cbff9ae54..d6fabb42f6bd 100644 --- a/label_studio/projects/api.py +++ b/label_studio/projects/api.py @@ -49,7 +49,7 @@ from rest_framework.response import Response from rest_framework.settings import api_settings from rest_framework.views import exception_handler -from tasks.models import Task +from tasks.models import Task, Annotation from tasks.serializers import ( NextTaskSerializer, TaskSerializer, @@ -920,3 +920,44 @@ def delete(self, request, *args, **kwargs): count = project.delete_predictions(model_version=model_version) return Response(data=count) + + +@method_decorator( + name='get', + decorator=extend_schema( + tags=['Projects'], + summary='List unique annotators for project', + description='Return a list of unique user IDs who have submitted annotations in the specified project.', + responses={ + 200: OpenApiResponse( + description='List of annotator user IDs', + response={ + 'type': 'object', + 'properties': { + 'annotators': { + 'type': 'array', + 'items': {'type': 'integer'}, + } + }, + }, + ) + }, + extensions={ + 'x-fern-sdk-group-name': 'projects', + 'x-fern-sdk-method-name': 'annotators', + 'x-fern-audiences': ['public'], + }, + ), +) +class ProjectAnnotatorsAPI(generics.RetrieveAPIView): + permission_required = all_permissions.projects_view + queryset = Project.objects.all() + + def get(self, request, *args, **kwargs): + project = self.get_object() + annotator_ids = list( + Annotation.objects.filter(project=project, completed_by_id__isnull=False) + .values_list('completed_by_id', flat=True) + .distinct() + ) + return Response({'annotators': annotator_ids}) diff --git a/label_studio/projects/urls.py b/label_studio/projects/urls.py index 0db84b70ee4d..fb4c1fe0c83a 100644 --- a/label_studio/projects/urls.py +++ b/label_studio/projects/urls.py @@ -45,6 +45,8 @@ path('/sample-task/', api.ProjectSampleTask.as_view(), name='project-sample-task'), # List available model versions path('/model-versions/', api.ProjectModelVersions.as_view(), name='project-model-versions'), + # List all annotators for project + path('/annotators/', api.ProjectAnnotatorsAPI.as_view(), name='project-annotators'), ] _api_urlpatterns_templates = [ From 338e181d2a871e3cf6e115b43c6c34c0d26750af Mon Sep 17 00:00:00 2001 From: hakan458 Date: Tue, 23 Sep 2025 13:27:58 -0700 Subject: [PATCH 02/21] update all urls --- label_studio/core/all_urls.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/label_studio/core/all_urls.json b/label_studio/core/all_urls.json index de24d53cc73d..fa99c1204348 100644 --- a/label_studio/core/all_urls.json +++ b/label_studio/core/all_urls.json @@ -215,6 +215,12 @@ "name": "projects:api:project-model-versions", "decorators": "" }, + { + "url": "/api/projects//annotators/", + "module": "projects.api.ProjectAnnotatorsAPI", + "name": "projects:api:project-annotators", + "decorators": "" + }, { "url": "/api/templates/", "module": "projects.api.TemplateListAPI", @@ -1843,19 +1849,13 @@ }, { "url": "/django-rq/", - "module": "django_rq.stats_views.stats", + "module": "django_rq.views.stats", "name": "rq_home", "decorators": "" }, - { - "url": "/django-rq/stats.json/", - "module": "django_rq.stats_views.stats_json", - "name": "rq_home_json", - "decorators": "" - }, { "url": "/django-rq/stats.json//", - "module": "django_rq.stats_views.stats_json", + "module": "django_rq.views.stats_json", "name": "rq_home_json", "decorators": "" }, From fef59764e335d5663639cb543c0e68280402fcbe Mon Sep 17 00:00:00 2001 From: hakan458 Date: Wed, 24 Sep 2025 12:59:07 -0700 Subject: [PATCH 03/21] return simpleuser from annotators api --- label_studio/projects/api.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/label_studio/projects/api.py b/label_studio/projects/api.py index d6fabb42f6bd..0b3e48001875 100644 --- a/label_studio/projects/api.py +++ b/label_studio/projects/api.py @@ -56,6 +56,8 @@ TaskSimpleSerializer, TaskWithAnnotationsAndPredictionsAndDraftsSerializer, ) +from users.models import User +from users.serializers import UserSimpleSerializer from webhooks.models import WebhookAction from webhooks.utils import api_webhook, api_webhook_for_delete, emit_webhooks_for_instance @@ -926,20 +928,12 @@ def delete(self, request, *args, **kwargs): name='get', decorator=extend_schema( tags=['Projects'], - summary='List unique annotators for project', - description='Return a list of unique user IDs who have submitted annotations in the specified project.', + summary='List annotators for project', + description='Return users who have submitted annotations in the specified project.', responses={ 200: OpenApiResponse( - description='List of annotator user IDs', - response={ - 'type': 'object', - 'properties': { - 'annotators': { - 'type': 'array', - 'items': {'type': 'integer'}, - } - }, - }, + description='List of annotator users', + response=UserSimpleSerializer(many=True), ) }, extensions={ @@ -960,4 +954,6 @@ def get(self, request, *args, **kwargs): .values_list('completed_by_id', flat=True) .distinct() ) - return Response({'annotators': annotator_ids}) + users = User.objects.filter(id__in=annotator_ids).prefetch_related('om_through').order_by('id') + data = UserSimpleSerializer(users, many=True, context={'request': request}).data + return Response(data) From ca918cff46bc9124298246b285b695cb10504b83 Mon Sep 17 00:00:00 2001 From: hakan458 Date: Wed, 24 Sep 2025 14:50:03 -0700 Subject: [PATCH 04/21] add migration for existing views --- ...agreement_selected_annotators_to_unique.py | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py diff --git a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py new file mode 100644 index 000000000000..29fa2cb04aa5 --- /dev/null +++ b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py @@ -0,0 +1,99 @@ +from django.db import migrations +from django.apps import apps as django_apps +from core.models import AsyncMigrationStatus +from core.redis import start_job_async_or_sync + +migration_name = '0016_migrate_agreement_selected_annotators_to_unique' + + +def forward_migration(): + migration, created = AsyncMigrationStatus.objects.get_or_create( + name=migration_name, + defaults={'status': AsyncMigrationStatus.STATUS_STARTED} + ) + if not created: + return # already in progress or done + + # Look up models at runtime inside the worker process + View = django_apps.get_model('data_manager', 'View') + Annotation = django_apps.get_model('tasks', 'Annotation') + + # Cache unique annotators per project_id to avoid repetitive queries + project_to_unique_annotators = {} + + # Iterate using values() to avoid loading full model instances + # Fetch only the fields we need + qs = View.objects.all().values('id', 'project_id', 'data') + + updated = 0 + for row in qs.iterator(chunk_size=1000): + view_id = row['id'] + project_id = row['project_id'] + data = row.get('data') or {} + + agreement = data.get('agreement_selected') + if not isinstance(agreement, dict): + continue + + # Only migrate views that actually have annotators key present + existing_annotators = agreement.get('annotators', None) + if existing_annotators is None: + continue + + # Compute unique annotators for this project (once per project) + if project_id not in project_to_unique_annotators: + unique_ids = list( + Annotation.objects + .filter(project_id=project_id, completed_by_id__isnull=False) + .values_list('completed_by_id', flat=True) + .distinct() + ) + # Normalize to unique ints + unique_ids = {int(u) for u in unique_ids} + project_to_unique_annotators[project_id] = unique_ids + + new_annotators = project_to_unique_annotators[project_id] + + # If no change, skip update + old_set = {int(a) for a in (existing_annotators or [])} + if set(new_annotators) == old_set: + continue + + # Prepare new data JSON + new_data = dict(data) + new_agreement = dict(agreement) + new_agreement['annotators'] = list(new_annotators) + new_data['agreement_selected'] = new_agreement + + # Update only the JSON field via update(); do not load model instance or call save() + View.objects.filter(id=view_id).update(data=new_data) + updated += 1 + + if updated: + print(f'{migration_name} Updated {updated} View rows') + + migration.status = AsyncMigrationStatus.STATUS_FINISHED + migration.save(update_fields=['status']) + +def forwards(apps, schema_editor): + start_job_async_or_sync(forward_migration) + + +def backwards(apps, schema_editor): + # Irreversible: we cannot reconstruct the previous annotator lists safely + pass + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ('data_manager', '0015_alter_view_options'), + ('tasks', '0001_initial'), # Historical dependency to ensure Annotation exists + ] + + operations = [ + migrations.RunPython(forwards, backwards), + ] + + From db553b7f6dc861f7d7c53643b94fd5ace26ae3b9 Mon Sep 17 00:00:00 2001 From: hakan458 Date: Wed, 24 Sep 2025 15:14:57 -0700 Subject: [PATCH 05/21] fix migration deps --- .../0016_migrate_agreement_selected_annotators_to_unique.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py index 29fa2cb04aa5..c2038b47b404 100644 --- a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py +++ b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py @@ -88,8 +88,7 @@ class Migration(migrations.Migration): atomic = False dependencies = [ - ('data_manager', '0015_alter_view_options'), - ('tasks', '0001_initial'), # Historical dependency to ensure Annotation exists + ('data_manager', '0015_alter_view_options') ] operations = [ From 603608c8deaa5372e9af4b0a3ee96d5cade853d7 Mon Sep 17 00:00:00 2001 From: hakan458 Date: Wed, 24 Sep 2025 15:21:26 -0700 Subject: [PATCH 06/21] fix urls --- label_studio/core/all_urls.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/label_studio/core/all_urls.json b/label_studio/core/all_urls.json index fa99c1204348..7c5079331fb8 100644 --- a/label_studio/core/all_urls.json +++ b/label_studio/core/all_urls.json @@ -1849,13 +1849,19 @@ }, { "url": "/django-rq/", - "module": "django_rq.views.stats", + "module": "django_rq.stats_views.stats", "name": "rq_home", "decorators": "" }, + { + "url": "/django-rq/stats.json/", + "module": "django_rq.stats_views.stats_json", + "name": "rq_home_json", + "decorators": "" + }, { "url": "/django-rq/stats.json//", - "module": "django_rq.views.stats_json", + "module": "django_rq.stats_views.stats_json", "name": "rq_home_json", "decorators": "" }, From b2c4a84acb459222d05e7ef34de6de195985d3c2 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Wed, 24 Sep 2025 22:46:03 +0000 Subject: [PATCH 07/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/17991622496 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2f388c8a415d..638b52cd8ea5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "7a4d5c352e428b059d7111086e93f108e47cc630.zip", hash = "sha256:fdc96110b7f7ab8553562ad0c4b07ea02e4c38ad69e79d863c64e6fda81e8319"}, + {file = "fa1a65a3a0cf488d81e6d34997f6edc27d4061ec.zip", hash = "sha256:61279dc55de8e1e09b3e4d72bc59fb1aa790a47c65060c2a92ab0ad576772fd1"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/7a4d5c352e428b059d7111086e93f108e47cc630.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/fa1a65a3a0cf488d81e6d34997f6edc27d4061ec.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "8e4c56e5d1e9d3a45d50411209c5c9340f32266603e072a11e312f93a6a2564e" +content-hash = "065ed260189efce97c1713190269a1dedad1891d4ebabd2233a2bda135381b8b" diff --git a/pyproject.toml b/pyproject.toml index 9c95b75d1258..47d244d17ffb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/7a4d5c352e428b059d7111086e93f108e47cc630.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/fa1a65a3a0cf488d81e6d34997f6edc27d4061ec.zip", ## HumanSignal repo dependencies :end ] From 048ea697394da2f20aaf273302c768d621c8e68a Mon Sep 17 00:00:00 2001 From: Hakan Erol Date: Wed, 24 Sep 2025 15:54:21 -0700 Subject: [PATCH 08/21] update api description Co-authored-by: Jo Booth --- label_studio/projects/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/label_studio/projects/api.py b/label_studio/projects/api.py index 0b3e48001875..2d172b9d6ffc 100644 --- a/label_studio/projects/api.py +++ b/label_studio/projects/api.py @@ -928,8 +928,8 @@ def delete(self, request, *args, **kwargs): name='get', decorator=extend_schema( tags=['Projects'], - summary='List annotators for project', - description='Return users who have submitted annotations in the specified project.', + summary='List unique annotators for project', + description='Return unique users who have submitted annotations in the specified project.', responses={ 200: OpenApiResponse( description='List of annotator users', From aca3029ab97facd163ea694d6b5fceb66370af2b Mon Sep 17 00:00:00 2001 From: Hakan Erol Date: Wed, 24 Sep 2025 15:54:42 -0700 Subject: [PATCH 09/21] Update label_studio/projects/api.py Co-authored-by: Jo Booth --- label_studio/projects/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/label_studio/projects/api.py b/label_studio/projects/api.py index 2d172b9d6ffc..0c4b12242232 100644 --- a/label_studio/projects/api.py +++ b/label_studio/projects/api.py @@ -938,7 +938,7 @@ def delete(self, request, *args, **kwargs): }, extensions={ 'x-fern-sdk-group-name': 'projects', - 'x-fern-sdk-method-name': 'annotators', + 'x-fern-sdk-method-name': 'list_unique_annotators', 'x-fern-audiences': ['public'], }, ), From bc8b13c5f1141bf202ad2f6417320c368ea68ec5 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Wed, 24 Sep 2025 23:04:58 +0000 Subject: [PATCH 10/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/17991901074 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 638b52cd8ea5..7a3eb34bbd03 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "fa1a65a3a0cf488d81e6d34997f6edc27d4061ec.zip", hash = "sha256:61279dc55de8e1e09b3e4d72bc59fb1aa790a47c65060c2a92ab0ad576772fd1"}, + {file = "33498956f9972670fb6acd519b9ffb9ed7519ff1.zip", hash = "sha256:a372ca7ae45d0a70a31d22c094659f71dfd6a41235bb0a581e8776bc400221c5"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/fa1a65a3a0cf488d81e6d34997f6edc27d4061ec.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/33498956f9972670fb6acd519b9ffb9ed7519ff1.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "065ed260189efce97c1713190269a1dedad1891d4ebabd2233a2bda135381b8b" +content-hash = "88a76f1a92582a0565a30f83792735a1693cd08fefc1d9d287245a340891573a" diff --git a/pyproject.toml b/pyproject.toml index 47d244d17ffb..09c0e0ad7be7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/fa1a65a3a0cf488d81e6d34997f6edc27d4061ec.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/33498956f9972670fb6acd519b9ffb9ed7519ff1.zip", ## HumanSignal repo dependencies :end ] From 7ce50098ed0bf3515ccfa8dfe0ad000e28bf8d31 Mon Sep 17 00:00:00 2001 From: hakan458 Date: Wed, 24 Sep 2025 16:18:33 -0700 Subject: [PATCH 11/21] updates --- ...e_agreement_selected_annotators_to_unique.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py index c2038b47b404..eced2116155e 100644 --- a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py +++ b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py @@ -1,4 +1,5 @@ from django.db import migrations +from copy import deepcopy from django.apps import apps as django_apps from core.models import AsyncMigrationStatus from core.redis import start_job_async_or_sync @@ -26,7 +27,7 @@ def forward_migration(): qs = View.objects.all().values('id', 'project_id', 'data') updated = 0 - for row in qs.iterator(chunk_size=1000): + for row in qs: view_id = row['id'] project_id = row['project_id'] data = row.get('data') or {} @@ -42,31 +43,29 @@ def forward_migration(): # Compute unique annotators for this project (once per project) if project_id not in project_to_unique_annotators: - unique_ids = list( + unique_ids = set( Annotation.objects .filter(project_id=project_id, completed_by_id__isnull=False) .values_list('completed_by_id', flat=True) .distinct() ) # Normalize to unique ints - unique_ids = {int(u) for u in unique_ids} project_to_unique_annotators[project_id] = unique_ids new_annotators = project_to_unique_annotators[project_id] # If no change, skip update old_set = {int(a) for a in (existing_annotators or [])} - if set(new_annotators) == old_set: + if new_annotators == old_set: continue - # Prepare new data JSON - new_data = dict(data) - new_agreement = dict(agreement) - new_agreement['annotators'] = list(new_annotators) - new_data['agreement_selected'] = new_agreement + new_data = deepcopy(data) + new_data['agreement_selected']['annotators'] = list(new_annotators) # Update only the JSON field via update(); do not load model instance or call save() View.objects.filter(id=view_id).update(data=new_data) + print(f'Updated View {view_id} agreement selected annotators to {list(new_annotators)}') + print(f'Old annotator length: {len(old_set)}, new annotator length: {len(new_annotators)}') updated += 1 if updated: From ab3a4868d20c78114fa9085ee24141e17bf7efa7 Mon Sep 17 00:00:00 2001 From: hakan458 Date: Wed, 24 Sep 2025 16:37:45 -0700 Subject: [PATCH 12/21] add sdk test --- label_studio/projects/api.py | 2 +- .../tests/sdk/test_project_annotators_api.py | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 label_studio/tests/sdk/test_project_annotators_api.py diff --git a/label_studio/projects/api.py b/label_studio/projects/api.py index 0c4b12242232..62b4d60a78a6 100644 --- a/label_studio/projects/api.py +++ b/label_studio/projects/api.py @@ -49,7 +49,7 @@ from rest_framework.response import Response from rest_framework.settings import api_settings from rest_framework.views import exception_handler -from tasks.models import Task, Annotation +from tasks.models import Annotation, Task from tasks.serializers import ( NextTaskSerializer, TaskSerializer, diff --git a/label_studio/tests/sdk/test_project_annotators_api.py b/label_studio/tests/sdk/test_project_annotators_api.py new file mode 100644 index 000000000000..25fabfd4905a --- /dev/null +++ b/label_studio/tests/sdk/test_project_annotators_api.py @@ -0,0 +1,45 @@ +import pytest +from label_studio_sdk.client import LabelStudio +from tasks.models import Annotation + +from label_studio.tests.sdk.common import LABEL_CONFIG_AND_TASKS + +pytestmark = pytest.mark.django_db + + +def test_project_annotators_sdk(django_live_url, business_client): + ls = LabelStudio(base_url=django_live_url, api_key=business_client.api_key) + + # Create project via SDK + proj = ls.projects.create(title='Annotators Project', label_config=LABEL_CONFIG_AND_TASKS['label_config']) + + # Import two tasks + ls.projects.import_tasks( + id=proj.id, + request=[ + {'data': {'my_text': 't1'}}, + {'data': {'my_text': 't2'}}, + ], + ) + + # Get created tasks + tasks = list(ls.tasks.list(project=proj.id)) + + # Create two users via SDK + u2 = ls.users.create(email='a2@example.com', username='annotator2', first_name='A', last_name='Two') + u3 = ls.users.create(email='a3@example.com', username='annotator3', first_name='A', last_name='Three') + + # Add annotations directly (SDK doesn't expose annotation create easily with arbitrary user) + # Use ORM for completed_by set to the two users + Annotation.objects.create( + task_id=tasks[0].id, project_id=proj.id, completed_by_id=business_client.user.id, result=[{'r': 1}] + ) + Annotation.objects.create(task_id=tasks[1].id, project_id=proj.id, completed_by_id=u2.id, result=[{'r': 2}]) + + # Call annotators API via SDK wrapper + resp = list(ls.projects.list_unique_annotators(id=proj.id)) + + returned_ids = [u.id for u in resp] + assert sorted(returned_ids) == sorted([business_client.user.id, u2.id]) + assert returned_ids == sorted(returned_ids) + assert u3.id not in returned_ids # no annotations created for this user From 2c630540053c7fb5db711109358a80326288f033 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Thu, 25 Sep 2025 13:16:43 +0000 Subject: [PATCH 13/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18008642903 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7a3eb34bbd03..97fb9f43590c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "33498956f9972670fb6acd519b9ffb9ed7519ff1.zip", hash = "sha256:a372ca7ae45d0a70a31d22c094659f71dfd6a41235bb0a581e8776bc400221c5"}, + {file = "b66ff6324b154b6b83b702c67e52029a93f6af8a.zip", hash = "sha256:b76fd21fa770597184f315f7acfebdaf60ef765307f1716a97eebcbcd0ba991b"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/33498956f9972670fb6acd519b9ffb9ed7519ff1.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/b66ff6324b154b6b83b702c67e52029a93f6af8a.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "88a76f1a92582a0565a30f83792735a1693cd08fefc1d9d287245a340891573a" +content-hash = "fe51d2aee9e451147c84628fd5f8f0f4bf7fb05f14512ba5165b7e46b972a55c" diff --git a/pyproject.toml b/pyproject.toml index 09c0e0ad7be7..57f89e756b54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/33498956f9972670fb6acd519b9ffb9ed7519ff1.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/b66ff6324b154b6b83b702c67e52029a93f6af8a.zip", ## HumanSignal repo dependencies :end ] From 4d5a89274485bdecca0c68def66c625e34c27a05 Mon Sep 17 00:00:00 2001 From: yyassi-heartex Date: Thu, 25 Sep 2025 13:38:06 +0000 Subject: [PATCH 14/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18009284610 From 7794e152b99f2293ccb569f35cf3142d8745ae2a Mon Sep 17 00:00:00 2001 From: yyassi-heartex Date: Thu, 25 Sep 2025 14:09:19 +0000 Subject: [PATCH 15/21] Apply pre-commit linters Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18010152838 --- label_studio/tests/sdk/test_project_annotators_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/label_studio/tests/sdk/test_project_annotators_api.py b/label_studio/tests/sdk/test_project_annotators_api.py index 25fabfd4905a..6cd48f3ae2ee 100644 --- a/label_studio/tests/sdk/test_project_annotators_api.py +++ b/label_studio/tests/sdk/test_project_annotators_api.py @@ -42,4 +42,4 @@ def test_project_annotators_sdk(django_live_url, business_client): returned_ids = [u.id for u in resp] assert sorted(returned_ids) == sorted([business_client.user.id, u2.id]) assert returned_ids == sorted(returned_ids) - assert u3.id not in returned_ids # no annotations created for this user + assert u3.id not in returned_ids # no annotations created for this user From 909ef324e5949439743e7f0e6313d24c7b0d71eb Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Thu, 25 Sep 2025 14:19:39 +0000 Subject: [PATCH 16/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18010479122 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 97fb9f43590c..0ae7058cc5d7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "b66ff6324b154b6b83b702c67e52029a93f6af8a.zip", hash = "sha256:b76fd21fa770597184f315f7acfebdaf60ef765307f1716a97eebcbcd0ba991b"}, + {file = "6cc5fa2b8189aab04e8e805663d8a32db44d93ac.zip", hash = "sha256:ab7d45453f25de3792fde2171b9e951986c0b5029fd707308c4684d9824dc2c1"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/b66ff6324b154b6b83b702c67e52029a93f6af8a.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/6cc5fa2b8189aab04e8e805663d8a32db44d93ac.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "fe51d2aee9e451147c84628fd5f8f0f4bf7fb05f14512ba5165b7e46b972a55c" +content-hash = "c0261d7972e01001ee78dd9134a046cdcdc77922aa7863971c243ecbe7997993" diff --git a/pyproject.toml b/pyproject.toml index 57f89e756b54..2b322edb44b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/b66ff6324b154b6b83b702c67e52029a93f6af8a.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/6cc5fa2b8189aab04e8e805663d8a32db44d93ac.zip", ## HumanSignal repo dependencies :end ] From 632f900ff128c9d4e2fbf6d5643453ee48be99e8 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Thu, 25 Sep 2025 16:37:00 +0000 Subject: [PATCH 17/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18014299834 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0ae7058cc5d7..2890bb4723b4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "6cc5fa2b8189aab04e8e805663d8a32db44d93ac.zip", hash = "sha256:ab7d45453f25de3792fde2171b9e951986c0b5029fd707308c4684d9824dc2c1"}, + {file = "9cc1a9796ec8e8dc7f8f1946ccdf62dca2a92954.zip", hash = "sha256:284f1a7b2a51129bcceac1a361e3d7a681bf8ac46489b8440f1ebb726fd88ccd"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/6cc5fa2b8189aab04e8e805663d8a32db44d93ac.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/9cc1a9796ec8e8dc7f8f1946ccdf62dca2a92954.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "c0261d7972e01001ee78dd9134a046cdcdc77922aa7863971c243ecbe7997993" +content-hash = "017cbdf77b9fd13ac5ff1b92767395b7179ac1b64da3106011b6b54b66c0920a" diff --git a/pyproject.toml b/pyproject.toml index 2b322edb44b1..14e8cce0414b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/6cc5fa2b8189aab04e8e805663d8a32db44d93ac.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/9cc1a9796ec8e8dc7f8f1946ccdf62dca2a92954.zip", ## HumanSignal repo dependencies :end ] From 6b643ac7fd59b7799b2caa2c0317e419c2839798 Mon Sep 17 00:00:00 2001 From: hakan458 Date: Thu, 25 Sep 2025 09:46:01 -0700 Subject: [PATCH 18/21] updates --- ...migrate_agreement_selected_annotators_to_unique.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py index eced2116155e..0a8906893d74 100644 --- a/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py +++ b/label_studio/data_manager/migrations/0016_migrate_agreement_selected_annotators_to_unique.py @@ -3,9 +3,12 @@ from django.apps import apps as django_apps from core.models import AsyncMigrationStatus from core.redis import start_job_async_or_sync +import logging migration_name = '0016_migrate_agreement_selected_annotators_to_unique' +logger = logging.getLogger(__name__) + def forward_migration(): migration, created = AsyncMigrationStatus.objects.get_or_create( @@ -64,18 +67,18 @@ def forward_migration(): # Update only the JSON field via update(); do not load model instance or call save() View.objects.filter(id=view_id).update(data=new_data) - print(f'Updated View {view_id} agreement selected annotators to {list(new_annotators)}') - print(f'Old annotator length: {len(old_set)}, new annotator length: {len(new_annotators)}') + logger.info(f'Updated View {view_id} agreement selected annotators to {list(new_annotators)}') + logger.info(f'Old annotator length: {len(old_set)}, new annotator length: {len(new_annotators)}') updated += 1 if updated: - print(f'{migration_name} Updated {updated} View rows') + logger.info(f'{migration_name} Updated {updated} View rows') migration.status = AsyncMigrationStatus.STATUS_FINISHED migration.save(update_fields=['status']) def forwards(apps, schema_editor): - start_job_async_or_sync(forward_migration) + start_job_async_or_sync(forward_migration, queue_name='low') def backwards(apps, schema_editor): From 2275eb28a764660703e6524a98175ec273bcd72a Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Thu, 25 Sep 2025 16:50:29 +0000 Subject: [PATCH 19/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18014621852 From 0d6426ff46630b53e11e15df59da0f970d703bd3 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Thu, 25 Sep 2025 16:55:26 +0000 Subject: [PATCH 20/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18014691703 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2890bb4723b4..6151afbfe95b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "9cc1a9796ec8e8dc7f8f1946ccdf62dca2a92954.zip", hash = "sha256:284f1a7b2a51129bcceac1a361e3d7a681bf8ac46489b8440f1ebb726fd88ccd"}, + {file = "9c96857fc48364926e6eecbb52987ac1884e7fb1.zip", hash = "sha256:692243a545f3a3c9c341b03952099b719941018e8ab0e18a37425e4fbdacda49"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/9cc1a9796ec8e8dc7f8f1946ccdf62dca2a92954.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/9c96857fc48364926e6eecbb52987ac1884e7fb1.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "017cbdf77b9fd13ac5ff1b92767395b7179ac1b64da3106011b6b54b66c0920a" +content-hash = "95b4144e42e470c275210492eb91aff281f680f80bc1acdfd5da7da09a380c07" diff --git a/pyproject.toml b/pyproject.toml index 14e8cce0414b..efb856d9717f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/9cc1a9796ec8e8dc7f8f1946ccdf62dca2a92954.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/9c96857fc48364926e6eecbb52987ac1884e7fb1.zip", ## HumanSignal repo dependencies :end ] From ac3795919ea33a2af1b9e26ac24ea12c2df17327 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Thu, 25 Sep 2025 17:33:55 +0000 Subject: [PATCH 21/21] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/18015708891 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6151afbfe95b..e30770c797d0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2152,7 +2152,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "9c96857fc48364926e6eecbb52987ac1884e7fb1.zip", hash = "sha256:692243a545f3a3c9c341b03952099b719941018e8ab0e18a37425e4fbdacda49"}, + {file = "48b3458394e45c6e78997795c03a2be3adb2d533.zip", hash = "sha256:49481d354e72133fd78544a83aea947e683315e193f15e1826f6a57fa2288b7d"}, ] [package.dependencies] @@ -2180,7 +2180,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/9c96857fc48364926e6eecbb52987ac1884e7fb1.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/48b3458394e45c6e78997795c03a2be3adb2d533.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5126,4 +5126,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "95b4144e42e470c275210492eb91aff281f680f80bc1acdfd5da7da09a380c07" +content-hash = "2fca5b38d57892103ce9ee8476e0fc0a338ec351bdde5404e3dc3e8e91c7b4d9" diff --git a/pyproject.toml b/pyproject.toml index efb856d9717f..4fb9333ddae1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ "tldextract (>=5.1.3)", "uuid-utils (>=0.11.0,<1.0.0)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/9c96857fc48364926e6eecbb52987ac1884e7fb1.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/48b3458394e45c6e78997795c03a2be3adb2d533.zip", ## HumanSignal repo dependencies :end ]