"""CVAT API project methods.""" from __future__ import annotations from typing import TYPE_CHECKING from metrics_evaluation.schema.cvat import CvatApiLabelDefinition, CvatApiProjectDetails 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 ProjectsMethods, 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 ProjectsMethods: """Project-level operations for CVAT API.""" def __init__(self, client: "CvatApiClient"): """Initialize project 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, token: str | None = None) -> list[CvatApiProjectDetails]: """List all projects accessible to the user. Args: token: Authentication token (optional) Returns: List of project details objects """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/projects?page_size=1000" response = self.client._make_request( method="GET", url=url, headers=headers, resource_name="projects list", ) response_data = response.json() return [ CvatApiProjectDetails.model_validate(project) for project in response_data.get("results", []) ] @retry_with_backoff(max_retries=3, initial_delay=1.0) def get_project_details( self, project_id: int, token: str | None = None ) -> CvatApiProjectDetails: """Fetch project details. Args: project_id: The ID of the project token: Authentication token (optional) Returns: Project details object """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/projects/{project_id}" return self.client._make_request( method="GET", url=url, headers=headers, resource_name="project", resource_id=project_id, response_model=CvatApiProjectDetails, ) @retry_with_backoff(max_retries=3, initial_delay=1.0) def get_project_labels( self, project_id: int, token: str | None = None ) -> list[CvatApiLabelDefinition]: """Fetch labels for a project. Args: project_id: The ID of the project token: Authentication token (optional) Returns: List of label definitions """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/labels?project_id={project_id}&page_size=1000" response = self.client._make_request( method="GET", url=url, headers=headers, resource_name="project labels", resource_id=project_id, ) response_data = response.json() return [ CvatApiLabelDefinition.model_validate(label) for label in response_data.get("results", []) ] @retry_with_backoff(max_retries=3, initial_delay=1.0) def get_project_job_ids( self, project_id: int, token: str | None = None ) -> list[int]: """Fetch all job IDs associated with a project. Args: project_id: The ID of the project 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?project_id={project_id}&page_size=1000&org={self.client.cvat_organization}" response = self.client._make_request( method="GET", url=url, headers=headers, resource_name="project jobs", resource_id=project_id, response_model=CvatApiJobsListResponse, ) return [job.id for job in response.results] @retry_with_backoff(max_retries=3, initial_delay=1.0) def get_project_tasks(self, project_id: int, token: str | None = None) -> list[int]: """Fetch all task IDs for a project. Args: project_id: The ID of the project token: Authentication token (optional) Returns: List of task IDs """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/tasks?project_id={project_id}&page_size=1000" response = self.client._make_request( method="GET", url=url, headers=headers, resource_name="project tasks", resource_id=project_id, ) response_data = response.json() return [task["id"] for task in response_data.get("results", [])]