From 4180d1218be0950ea4eb875465cfbd2a5dc50d04 Mon Sep 17 00:00:00 2001 From: makseq Date: Wed, 20 Aug 2025 03:12:09 +0300 Subject: [PATCH 1/6] fix: BROS-339: Label stream shows 404 however there are postponed/skipped tasks --- label_studio/projects/functions/next_task.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/label_studio/projects/functions/next_task.py b/label_studio/projects/functions/next_task.py index eecb9f693950..70fb04303a38 100644 --- a/label_studio/projects/functions/next_task.py +++ b/label_studio/projects/functions/next_task.py @@ -7,6 +7,7 @@ from django.conf import settings from django.db.models import BooleanField, Case, Count, Exists, F, Max, OuterRef, Q, QuerySet, Value, When from django.db.models.fields import DecimalField +from core.utils.db import fast_first from projects.functions.stream_history import add_stream_history from projects.models import Project from tasks.models import Annotation, Task @@ -254,27 +255,33 @@ def get_next_task_without_dm_queue( return next_task, use_task_lock, queue_info -def skipped_queue(next_task, prepared_tasks, project, user, queue_info): +def skipped_queue(next_task, prepared_tasks, project, user, assigned_flag, queue_info): if not next_task and project.skip_queue == project.SkipQueue.REQUEUE_FOR_ME: q = Q(project=project, task__isnull=False, was_cancelled=True, task__is_labeled=False) skipped_tasks = user.annotations.filter(q).order_by('updated_at').values_list('task__pk', flat=True) if skipped_tasks.exists(): preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(skipped_tasks)]) skipped_tasks = prepared_tasks.filter(pk__in=skipped_tasks).order_by(preserved_order) - next_task = _get_first_unlocked(skipped_tasks, user) + if assigned_flag: + next_task = fast_first(skipped_tasks) + else: + next_task = _get_first_unlocked(skipped_tasks, user) queue_info = 'Skipped queue' return next_task, queue_info -def postponed_queue(next_task, prepared_tasks, project, user, queue_info): +def postponed_queue(next_task, prepared_tasks, project, user, assigned_flag, queue_info): if not next_task: q = Q(task__project=project, task__isnull=False, was_postponed=True, task__is_labeled=False) postponed_tasks = user.drafts.filter(q).order_by('updated_at').values_list('task__pk', flat=True) if postponed_tasks.exists(): preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(postponed_tasks)]) postponed_tasks = prepared_tasks.filter(pk__in=postponed_tasks).order_by(preserved_order) - next_task = _get_first_unlocked(postponed_tasks, user) + if assigned_flag: + next_task = fast_first(postponed_tasks) + else: + next_task = _get_first_unlocked(postponed_tasks, user) if next_task is not None: next_task.allow_postpone = False queue_info = 'Postponed draft queue' @@ -357,9 +364,9 @@ def get_next_task( not_solved_tasks, user_solved_tasks_array, prepared_tasks, user, project, queue_info ) - next_task, queue_info = postponed_queue(next_task, prepared_tasks, project, user, queue_info) + next_task, queue_info = postponed_queue(next_task, prepared_tasks, project, user, assigned_flag, queue_info) - next_task, queue_info = skipped_queue(next_task, prepared_tasks, project, user, queue_info) + next_task, queue_info = skipped_queue(next_task, prepared_tasks, project, user, assigned_flag, queue_info) if next_task and use_task_lock: # set lock for the task with TTL 3x time more then current average lead time (or 1 hour by default) From e9a998e90e4498387b28d8c625423e009da28d9f Mon Sep 17 00:00:00 2001 From: makseq Date: Wed, 20 Aug 2025 03:16:43 +0300 Subject: [PATCH 2/6] Add comment --- label_studio/projects/functions/next_task.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/label_studio/projects/functions/next_task.py b/label_studio/projects/functions/next_task.py index 70fb04303a38..45c3a081f3a5 100644 --- a/label_studio/projects/functions/next_task.py +++ b/label_studio/projects/functions/next_task.py @@ -262,6 +262,11 @@ def skipped_queue(next_task, prepared_tasks, project, user, assigned_flag, queue if skipped_tasks.exists(): preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(skipped_tasks)]) skipped_tasks = prepared_tasks.filter(pk__in=skipped_tasks).order_by(preserved_order) + + # for assigned annotators locks don't make sense, moreover, + # _get_first_unlocked breaks label stream for manual mode because + # it evaluates locks based on auto-mode logic and returns None + # when there are no more tasks to label in auto-mode if assigned_flag: next_task = fast_first(skipped_tasks) else: @@ -278,6 +283,11 @@ def postponed_queue(next_task, prepared_tasks, project, user, assigned_flag, que if postponed_tasks.exists(): preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(postponed_tasks)]) postponed_tasks = prepared_tasks.filter(pk__in=postponed_tasks).order_by(preserved_order) + + # for assigned annotators locks don't make sense, moreover, + # _get_first_unlocked breaks label stream for manual mode because + # it evaluates locks based on auto-mode logic and returns None + # when there are no more tasks to label in auto-mode if assigned_flag: next_task = fast_first(postponed_tasks) else: From a062f681aaddccf193cbe5050f57f2c37bfd55be Mon Sep 17 00:00:00 2001 From: makseq Date: Wed, 20 Aug 2025 13:25:07 +0300 Subject: [PATCH 3/6] Add test --- label_studio/projects/functions/next_task.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/label_studio/projects/functions/next_task.py b/label_studio/projects/functions/next_task.py index 45c3a081f3a5..fd7a998237c2 100644 --- a/label_studio/projects/functions/next_task.py +++ b/label_studio/projects/functions/next_task.py @@ -4,10 +4,10 @@ from core.feature_flags import flag_set from core.utils.common import conditional_atomic, db_is_not_sqlite, load_func +from core.utils.db import fast_first from django.conf import settings from django.db.models import BooleanField, Case, Count, Exists, F, Max, OuterRef, Q, QuerySet, Value, When from django.db.models.fields import DecimalField -from core.utils.db import fast_first from projects.functions.stream_history import add_stream_history from projects.models import Project from tasks.models import Annotation, Task @@ -262,9 +262,9 @@ def skipped_queue(next_task, prepared_tasks, project, user, assigned_flag, queue if skipped_tasks.exists(): preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(skipped_tasks)]) skipped_tasks = prepared_tasks.filter(pk__in=skipped_tasks).order_by(preserved_order) - + # for assigned annotators locks don't make sense, moreover, - # _get_first_unlocked breaks label stream for manual mode because + # _get_first_unlocked breaks label stream for manual mode because # it evaluates locks based on auto-mode logic and returns None # when there are no more tasks to label in auto-mode if assigned_flag: @@ -283,9 +283,9 @@ def postponed_queue(next_task, prepared_tasks, project, user, assigned_flag, que if postponed_tasks.exists(): preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(postponed_tasks)]) postponed_tasks = prepared_tasks.filter(pk__in=postponed_tasks).order_by(preserved_order) - + # for assigned annotators locks don't make sense, moreover, - # _get_first_unlocked breaks label stream for manual mode because + # _get_first_unlocked breaks label stream for manual mode because # it evaluates locks based on auto-mode logic and returns None # when there are no more tasks to label in auto-mode if assigned_flag: From 2c8487144a483b149acfe789daa744c1764d3a83 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Wed, 20 Aug 2025 19:13:21 +0000 Subject: [PATCH 4/6] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/17107953160 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5e9f227f24c0..43e48374f9fb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2136,7 +2136,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "739f3231a40c0903abb52936ed833c7d4a053595.zip", hash = "sha256:7d278a659312d8b299fa964572e5f1713d68aa38d41d3ba5d1539a1c4e118ccd"}, + {file = "28da9f0f1541f3de6af4a35e6ff9b3bce2d0a7bb.zip", hash = "sha256:d6641639c99194b4f5ada1024d78c86df0b84801b04ea5d28abe78ad1c5c3851"}, ] [package.dependencies] @@ -2164,7 +2164,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/739f3231a40c0903abb52936ed833c7d4a053595.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/28da9f0f1541f3de6af4a35e6ff9b3bce2d0a7bb.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5037,4 +5037,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "2cec409e00c0b54891e5299f4e71a4bbcd6955cb843ab8eb95960ff98c33ce0e" +content-hash = "8cf94e167f6bb1f8aee12da1f7c913ca43aec10fa32844968ae34b1dda0b0b71" diff --git a/pyproject.toml b/pyproject.toml index c0a48ed204de..0e70d1e68f04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ dependencies = [ "djangorestframework-simplejwt[crypto] (>=5.4.0,<6.0.0)", "tldextract (>=5.1.3)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/739f3231a40c0903abb52936ed833c7d4a053595.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/28da9f0f1541f3de6af4a35e6ff9b3bce2d0a7bb.zip", ## HumanSignal repo dependencies :end ] From 80a924e019f24f48aaab834a2f1bb50b3aded4a6 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Wed, 20 Aug 2025 19:19:51 +0000 Subject: [PATCH 5/6] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/17108088587 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 43e48374f9fb..2788e0a80b8d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2136,7 +2136,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "28da9f0f1541f3de6af4a35e6ff9b3bce2d0a7bb.zip", hash = "sha256:d6641639c99194b4f5ada1024d78c86df0b84801b04ea5d28abe78ad1c5c3851"}, + {file = "a5b9ba964ef5ae4e24e08e0590bb30e89518787c.zip", hash = "sha256:25a80bfda301da9da8cd8b0925829579480baa2ea3c75816c6beba0e92f355c0"}, ] [package.dependencies] @@ -2164,7 +2164,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/28da9f0f1541f3de6af4a35e6ff9b3bce2d0a7bb.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/a5b9ba964ef5ae4e24e08e0590bb30e89518787c.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5037,4 +5037,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "8cf94e167f6bb1f8aee12da1f7c913ca43aec10fa32844968ae34b1dda0b0b71" +content-hash = "685615902c61eb23c5b5d97ba937edf5605501e5a87447ef3689287dba2a05a2" diff --git a/pyproject.toml b/pyproject.toml index 0e70d1e68f04..70d66ab63109 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ dependencies = [ "djangorestframework-simplejwt[crypto] (>=5.4.0,<6.0.0)", "tldextract (>=5.1.3)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/28da9f0f1541f3de6af4a35e6ff9b3bce2d0a7bb.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/a5b9ba964ef5ae4e24e08e0590bb30e89518787c.zip", ## HumanSignal repo dependencies :end ] From 73053a5fd884870ef7c1a6d3a082ebe65e7f0a56 Mon Sep 17 00:00:00 2001 From: robot-ci-heartex Date: Wed, 20 Aug 2025 19:22:39 +0000 Subject: [PATCH 6/6] Sync Follow Merge dependencies Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/17108154170 --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2788e0a80b8d..99e679693dd2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2136,7 +2136,7 @@ optional = false python-versions = ">=3.9,<4" groups = ["main"] files = [ - {file = "a5b9ba964ef5ae4e24e08e0590bb30e89518787c.zip", hash = "sha256:25a80bfda301da9da8cd8b0925829579480baa2ea3c75816c6beba0e92f355c0"}, + {file = "8cd4269e3f84d02cfa5c85a0070bf10048f62a93.zip", hash = "sha256:0e8f20a29f1daef50cc9226bd70fd49c50b9ca448a2b912ce72ed6111a0ca94b"}, ] [package.dependencies] @@ -2164,7 +2164,7 @@ xmljson = "0.2.1" [package.source] type = "url" -url = "https://github.com/HumanSignal/label-studio-sdk/archive/a5b9ba964ef5ae4e24e08e0590bb30e89518787c.zip" +url = "https://github.com/HumanSignal/label-studio-sdk/archive/8cd4269e3f84d02cfa5c85a0070bf10048f62a93.zip" [[package]] name = "launchdarkly-server-sdk" @@ -5037,4 +5037,4 @@ uwsgi = ["pyuwsgi", "uwsgitop"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "685615902c61eb23c5b5d97ba937edf5605501e5a87447ef3689287dba2a05a2" +content-hash = "80cf2624871135aeb8d4336485cb84cf6f0de526ac32c6b2a5f9f0f167d543aa" diff --git a/pyproject.toml b/pyproject.toml index 70d66ab63109..9ddeb9d4890f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ dependencies = [ "djangorestframework-simplejwt[crypto] (>=5.4.0,<6.0.0)", "tldextract (>=5.1.3)", ## HumanSignal repo dependencies :start - "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/a5b9ba964ef5ae4e24e08e0590bb30e89518787c.zip", + "label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/8cd4269e3f84d02cfa5c85a0070bf10048f62a93.zip", ## HumanSignal repo dependencies :end ]