Skip to content
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
22 changes: 22 additions & 0 deletions roboflow/adapters/rfapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,28 @@ def delete_folder(api_key, workspace_url, group_id):
return response.json()


def add_projects_to_folder(api_key, workspace_url, group_id, project_ids):
"""PATCH /{ws}/groups/{id}/projects — add projects to a folder."""
response = requests.patch(
f"{API_URL}/{workspace_url}/groups/{group_id}/projects",
params={"api_key": api_key},
json={"projects": project_ids},
)
if response.status_code not in (200, 204):
raise RoboflowError(response.text)


def remove_projects_from_folder(api_key, workspace_url, group_id, project_ids):
"""DELETE /{ws}/groups/{id}/projects — remove projects from a folder."""
response = requests.delete(
f"{API_URL}/{workspace_url}/groups/{group_id}/projects",
params={"api_key": api_key},
json={"projects": project_ids},
)
if response.status_code not in (200, 204):
raise RoboflowError(response.text)


# ---------------------------------------------------------------------------
# Phase 2: Workflow endpoints
# ---------------------------------------------------------------------------
Expand Down
70 changes: 70 additions & 0 deletions roboflow/cli/handlers/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@ def delete_folder(
_delete_folder(args)


@folder_app.command("add-projects")
def add_projects(
ctx: typer.Context,
folder_id: Annotated[str, typer.Argument(help="Folder ID")],
projects: Annotated[str, typer.Argument(help="Comma-separated project IDs")],
) -> None:
"""Add projects to a folder."""
args = ctx_to_args(ctx, folder_id=folder_id, projects=projects)
_add_projects(args)


@folder_app.command("remove-projects")
def remove_projects(
ctx: typer.Context,
folder_id: Annotated[str, typer.Argument(help="Folder ID")],
projects: Annotated[str, typer.Argument(help="Comma-separated project IDs")],
) -> None:
"""Remove projects from a folder."""
args = ctx_to_args(ctx, folder_id=folder_id, projects=projects)
_remove_projects(args)


# ---------------------------------------------------------------------------
# Business logic (unchanged from argparse version)
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -200,3 +222,51 @@ def _delete_folder(args) -> None: # noqa: ANN001

data = {"status": "deleted"}
output(args, data, text=f"Deleted folder '{args.folder_id}'")


def _add_projects(args) -> None: # noqa: ANN001
import roboflow
from roboflow.cli._output import output, output_error, suppress_sdk_output

with suppress_sdk_output(args):
try:
rf = roboflow.Roboflow(api_key=args.api_key)
workspace = rf.workspace(args.workspace)
except Exception as exc:
output_error(args, str(exc))
return

project_ids = [p.strip() for p in args.projects.split(",")]

try:
workspace.add_projects_to_folder(args.folder_id, project_ids)
except Exception as exc:
output_error(args, str(exc), exit_code=1)
return

data = {"status": "added", "folder_id": args.folder_id, "projects": project_ids}
output(args, data, text=f"Added {len(project_ids)} project(s) to folder '{args.folder_id}'")


def _remove_projects(args) -> None: # noqa: ANN001
import roboflow
from roboflow.cli._output import output, output_error, suppress_sdk_output

with suppress_sdk_output(args):
try:
rf = roboflow.Roboflow(api_key=args.api_key)
workspace = rf.workspace(args.workspace)
except Exception as exc:
output_error(args, str(exc))
return

project_ids = [p.strip() for p in args.projects.split(",")]

try:
workspace.remove_projects_from_folder(args.folder_id, project_ids)
except Exception as exc:
output_error(args, str(exc), exit_code=1)
return

data = {"status": "removed", "folder_id": args.folder_id, "projects": project_ids}
output(args, data, text=f"Removed {len(project_ids)} project(s) from folder '{args.folder_id}'")
12 changes: 12 additions & 0 deletions roboflow/core/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,18 @@ def create_folder(self, name, parent_id=None, project_ids=None):

return rfapi.create_folder(self.__api_key, self.url, name, parent_id=parent_id, project_ids=project_ids)

def add_projects_to_folder(self, group_id, project_ids):
"""Add projects to an existing folder."""
from roboflow.adapters import rfapi

return rfapi.add_projects_to_folder(self.__api_key, self.url, group_id, project_ids)

def remove_projects_from_folder(self, group_id, project_ids):
"""Remove projects from a folder."""
from roboflow.adapters import rfapi

return rfapi.remove_projects_from_folder(self.__api_key, self.url, group_id, project_ids)

# -----------------------------------------------------------------
# Phase 2: Workflow management
# -----------------------------------------------------------------
Expand Down
Loading