Thibaut's picture
Fix SAM3 instance segmentation and update documentation
d032bfc
"""CVAT API task methods."""
from __future__ import annotations
from typing import TYPE_CHECKING
from metrics_evaluation.schema.cvat import CvatApiTaskDetails, CvatApiTaskMediasMetainformation
from .retry import retry_with_backoff
# TYPE_CHECKING is False at runtime but True during static type checking.
# This allows us to import CvatApiClient for type hints without creating a circular
# import (client.py imports TasksMethods, and we need CvatApiClient for type hints).
# Benefits:
# - Avoids circular import errors at runtime
# - Provides proper type checking during development
# - No performance overhead (import only happens during type checking, not at runtime)
# This is a recommended pattern in modern Python (PEP 484, PEP 563).
# -- Claude Code
if TYPE_CHECKING:
from .client import CvatApiClient
class TasksMethods:
"""Task-level operations for CVAT API."""
def __init__(self, client: "CvatApiClient"):
"""Initialize task methods with client reference.
Args:
client: Parent CvatApiClient instance
"""
self.client = client
@retry_with_backoff(max_retries=3, initial_delay=1.0)
def list(self, project_id: int | None = None, token: str | None = None) -> list[CvatApiTaskDetails]:
"""List all tasks, optionally filtered by project.
Args:
project_id: Filter by project ID (optional)
token: Authentication token (optional)
Returns:
List of task details objects
"""
headers = self.client._get_headers(token)
url = f"{self.client.cvat_host}/api/tasks?page_size=1000"
if project_id is not None:
url += f"&project_id={project_id}"
response = self.client._make_request(
method="GET",
url=url,
headers=headers,
resource_name="tasks list",
)
response_data = response.json()
return [
CvatApiTaskDetails.model_validate(task)
for task in response_data.get("results", [])
]
@retry_with_backoff(max_retries=3, initial_delay=1.0)
def get_task_details(
self, task_id: int, token: str | None = None
) -> CvatApiTaskDetails:
"""Fetch task details.
Args:
task_id: The ID of the task
token: Authentication token (optional)
Returns:
Task details object
"""
headers = self.client._get_headers(token)
url = f"{self.client.cvat_host}/api/tasks/{task_id}"
return self.client._make_request(
method="GET",
url=url,
headers=headers,
resource_name="task",
resource_id=task_id,
response_model=CvatApiTaskDetails,
)
@retry_with_backoff(max_retries=3, initial_delay=1.0)
def get_task_media_metainformation(
self, task_id: int, token: str | None = None
) -> CvatApiTaskMediasMetainformation:
"""Fetch task media metadata.
Args:
task_id: The ID of the task
token: Authentication token (optional)
Returns:
Task media metadata object
"""
headers = self.client._get_headers(token)
url = f"{self.client.cvat_host}/api/tasks/{task_id}/data/meta"
return self.client._make_request(
method="GET",
url=url,
headers=headers,
resource_name="task media metadata",
resource_id=task_id,
response_model=CvatApiTaskMediasMetainformation,
)
@retry_with_backoff(max_retries=3, initial_delay=1.0)
def get_task_job_ids(self, task_id: int, token: str | None = None) -> list[int]:
"""Fetch all job IDs for a task.
Args:
task_id: The ID of the task
token: Authentication token (optional)
Returns:
List of job IDs
"""
from metrics_evaluation.schema.cvat import CvatApiJobsListResponse
headers = self.client._get_headers(token)
url = f"{self.client.cvat_host}/api/jobs?task_id={task_id}&page_size=1000"
response = self.client._make_request(
method="GET",
url=url,
headers=headers,
resource_name="task jobs",
resource_id=task_id,
response_model=CvatApiJobsListResponse,
)
return [job.id for job in response.results]
@retry_with_backoff(max_retries=3, initial_delay=1.0)
def update_task(
self, task_id: int, task_data: CvatApiTaskDetails, token: str | None = None
) -> CvatApiTaskDetails:
"""Update task details (e.g., name, assignee, labels).
Args:
task_id: The ID of the task
task_data: Updated task details
token: Authentication token (optional)
Returns:
Updated task details object
"""
headers = self.client._get_headers(token)
url = f"{self.client.cvat_host}/api/tasks/{task_id}"
return self.client._make_request(
method="PATCH",
url=url,
headers=headers,
json_data=task_data.model_dump(exclude_unset=True),
resource_name="task",
resource_id=task_id,
response_model=CvatApiTaskDetails,
)
@retry_with_backoff(max_retries=3, initial_delay=1.0)
def get_frame(self, task_id: int, frame_number: int, token: str | None = None) -> bytes:
"""Download a single frame from a task.
Args:
task_id: The ID of the task
frame_number: The frame number to download
token: Authentication token (optional)
Returns:
Raw image bytes
"""
headers = self.client._get_headers(token)
url = f"{self.client.cvat_host}/api/tasks/{task_id}/data?type=frame&number={frame_number}&quality=original"
response = self.client._make_request(
method="GET",
url=url,
headers=headers,
resource_name="task frame",
resource_id=task_id,
)
return response.content