From 5bdba3b6d074e78e21660331a3ea274fb76fb7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Tue, 18 Aug 2026 11:02:25 +0200 Subject: [PATCH] release: prepare v1.4.1 --- CLI-COMMANDS.md | 2 ++ roboflow/__init__.py | 2 +- roboflow/cli/handlers/annotation.py | 23 +++++++++++--------- tests/cli/test_annotation_handler.py | 32 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/CLI-COMMANDS.md b/CLI-COMMANDS.md index 7fcadfe4..763cb790 100644 --- a/CLI-COMMANDS.md +++ b/CLI-COMMANDS.md @@ -181,6 +181,8 @@ roboflow annotation job admin-list -p my-project --limit 50 roboflow annotation job admin-get -p my-project roboflow annotation job admin-create -p my-project --batch \ --labeler a@co.com --reviewer b@co.com --name "Label round 1" +roboflow annotation job reassign-images -p my-project --image-id \ + --labeler a@co.com --yes roboflow annotation job create -p my-project --name "Label round 1" \ --batch --num-images 100 --labeler a@co.com --reviewer b@co.com roboflow annotation job images -p my-project diff --git a/roboflow/__init__.py b/roboflow/__init__.py index c248fa03..9ffd1639 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -21,7 +21,7 @@ CLIPModel = None # type: ignore[assignment,misc] GazeModel = None # type: ignore[assignment,misc] -__version__ = "1.4.0" +__version__ = "1.4.1" def check_key(api_key, model, notebook, num_retries=0): diff --git a/roboflow/cli/handlers/annotation.py b/roboflow/cli/handlers/annotation.py index de5b7dfd..2ceca056 100644 --- a/roboflow/cli/handlers/annotation.py +++ b/roboflow/cli/handlers/annotation.py @@ -268,18 +268,21 @@ def job_reassign_images( reviewer: Annotated[Optional[str], typer.Option(help="Reviewer email")] = None, instructions: Annotated[Optional[str], typer.Option(help="Labeling instructions")] = None, name: Annotated[Optional[str], typer.Option(help="Optional job name")] = None, + yes: Annotated[bool, typer.Option("-y", "--yes", help="Confirm reassignment of the images")] = False, ) -> None: """Create a job by explicitly reassigning images.""" - _simple_command( - ctx_to_args(ctx, project=project), - "reassign_annotation_job_images", - image_ids=image_ids, - labeler_email=labeler, - reviewer_email=reviewer, - instructions=instructions, - name=name, - success="Reassigned images to a new annotation job.", - ) + args = ctx_to_args(ctx, project=project, yes=yes) + if _confirm(args, "Remove these images from their current assignments and reassign them to a new job?"): + _simple_command( + args, + "reassign_annotation_job_images", + image_ids=image_ids, + labeler_email=labeler, + reviewer_email=reviewer, + instructions=instructions, + name=name, + success="Reassigned images to a new annotation job.", + ) @job_app.command("add-images") diff --git a/tests/cli/test_annotation_handler.py b/tests/cli/test_annotation_handler.py index e2a9676a..569ae6aa 100644 --- a/tests/cli/test_annotation_handler.py +++ b/tests/cli/test_annotation_handler.py @@ -422,6 +422,38 @@ def test_job_add_images_requires_yes_non_interactively(self, _resolve, mock_api) self.assertEqual(result.exit_code, 0, result.output) mock_api.assert_called_once_with("key", "ws", "proj", "job-1", image_ids=["image-1"]) + @patch("roboflow.adapters.rfapi.reassign_annotation_job_images") + @patch(_RESOLVE, return_value=("key", "ws", "proj")) + def test_job_reassign_images_requires_yes_non_interactively(self, _resolve, mock_api): + command = [ + "annotation", + "job", + "reassign-images", + "-p", + "ws/proj", + "--image-id", + "image-1", + "--labeler", + "labeler@example.com", + ] + result = runner.invoke(app, command) + self.assertEqual(result.exit_code, 1) + mock_api.assert_not_called() + + mock_api.return_value = {"jobId": "job-1"} + result = runner.invoke(app, [*command, "--yes"]) + self.assertEqual(result.exit_code, 0, result.output) + mock_api.assert_called_once_with( + "key", + "ws", + "proj", + image_ids=["image-1"], + labeler_email="labeler@example.com", + reviewer_email=None, + instructions=None, + name=None, + ) + @patch("roboflow.adapters.rfapi.accept_annotation_job_images") @patch(_RESOLVE, return_value=("key", "ws", "proj")) def test_job_accept_maps_lists_and_split_counts(self, _resolve, mock_api):