diff --git a/labellerr/core/annotation_templates/__init__.py b/labellerr/core/annotation_templates/__init__.py index f89441b..82a607a 100644 --- a/labellerr/core/annotation_templates/__init__.py +++ b/labellerr/core/annotation_templates/__init__.py @@ -1,8 +1,15 @@ from .base import LabellerrAnnotationTemplate -from ..schemas.annotation_templates import CreateTemplateParams, QuestionType, Option +from ..schemas.annotation_templates import ( + CreateTemplateParams, + QuestionType, + Option, + DatasetDataType, +) from .. import constants from ..client import LabellerrClient import uuid +from typing import List + __all__ = [ "LabellerrAnnotationTemplate", @@ -62,3 +69,31 @@ def create_template( client=client, annotation_template_id=response.get("response", None).get("template_id"), ) + + +def list_templates( + client: LabellerrClient, data_type: DatasetDataType +) -> List[LabellerrAnnotationTemplate]: + """ + List all annotation templates for a given data type + + :param client: The client to use for the request. + :param data_type: The data type to list templates for. + :return: A list of LabellerrAnnotationTemplate instances. + """ + unique_id = str(uuid.uuid4()) + url = ( + f"{constants.BASE_URL}/annotations/list_questions_templates?client_id={client.client_id}&data_type={data_type.value}" + f"&uuid={unique_id}" + ) + + response = client.make_request( + "GET", + url, + extra_headers={"content-type": "application/json"}, + request_id=unique_id, + ) + return [ + LabellerrAnnotationTemplate.from_annotation_template_data(client, **item) + for item in response.get("response", []) + ] diff --git a/labellerr/core/annotation_templates/base.py b/labellerr/core/annotation_templates/base.py index 21a24af..2a13909 100644 --- a/labellerr/core/annotation_templates/base.py +++ b/labellerr/core/annotation_templates/base.py @@ -24,8 +24,18 @@ def get_annotation_template(client: "LabellerrClient", annotation_template_id: s """Base class for all Labellerr projects with factory behavior""" - def __new__(cls, client: "LabellerrClient", annotation_template_id: str): - # Validate that the annotation template exists before creating the instance + def __new__( + cls, + client: "LabellerrClient", + annotation_template_id: str, + _skip_api_fetch: bool = False, + **kwargs, + ): + # If skip flag is set, create instance without API call + if _skip_api_fetch: + return super().__new__(cls) + + # Otherwise, fetch from API and validate annotation_template_data = cls.get_annotation_template( client, annotation_template_id ) @@ -37,14 +47,82 @@ def __new__(cls, client: "LabellerrClient", annotation_template_id: str): f"Annotation template with ID '{annotation_template_id}' does not exist or could not be retrieved." ) - # Create the instance only if validation passes - instance = super().__new__(cls) - # Store the data on the instance to avoid calling API again in __init__ - instance.__annotation_template_data = annotation_template_data - return instance + # Pass fetched data to __init__ via kwargs + kwargs["_fetched_data"] = annotation_template_data + return super().__new__(cls) - def __init__(self, client: "LabellerrClient", annotation_template_id: str): + def __init__( + self, + client: "LabellerrClient", + annotation_template_id: str, + _skip_api_fetch: bool = False, + **kwargs, + ): self.client = client - self.annotation_template_id = annotation_template_id - # Use the data already fetched in __new__ - self.annotation_template_data = self.__annotation_template_data + self.__annotation_template_id = annotation_template_id + + # Set __annotation_template_data from either source + if "_cached_data" in kwargs: + # Data provided directly (from factory method) + self.__annotation_template_data = kwargs["_cached_data"] + elif "_fetched_data" in kwargs: + # Data fetched in __new__ + self.__annotation_template_data = kwargs["_fetched_data"] + else: + # Fallback - shouldn't happen in normal usage + self.__annotation_template_data = {} + + @classmethod + def from_annotation_template_data(cls, client: "LabellerrClient", **kwargs): + """ + Create a LabellerrAnnotationTemplate instance from annotation template data. + + :param client: LabellerrClient instance + :param kwargs: Annotation template fields (template_id, template_name, questions, etc.) + :return: Instance of LabellerrAnnotationTemplate + """ + # Validate required fields + required_fields = { + "template_id", + "template_name", + "questions", + "created_at", + "created_by", + } + missing_fields = required_fields - set(kwargs.keys()) + if missing_fields: + raise ValueError( + f"Missing required fields in annotation_template_data: {missing_fields}" + ) + + # Create instance without API call - explicit flag makes intent clear + return cls( + client, + annotation_template_id=kwargs.get("template_id"), + _skip_api_fetch=True, + _cached_data=kwargs, + ) + + @property + def template_name(self): + return self.__annotation_template_data.get("template_name") + + @property + def data_type(self): + return self.__annotation_template_data.get("data_type") + + @property + def template_id(self): + return self.__annotation_template_id + + @property + def created_at(self): + return self.__annotation_template_data.get("created_at") + + @property + def created_by(self): + return self.__annotation_template_data.get("created_by") + + @property + def questions(self): + return self.__annotation_template_data.get("questions") diff --git a/labellerr/core/projects/__init__.py b/labellerr/core/projects/__init__.py index 2c737be..89ba938 100644 --- a/labellerr/core/projects/__init__.py +++ b/labellerr/core/projects/__init__.py @@ -10,6 +10,7 @@ from .document_project import DocucmentProject as LabellerrDocumentProject from .image_project import ImageProject as LabellerrImageProject from .video_project import VideoProject as LabellerrVideoProject +from .text_project import TextProject as LabellerrTextProject from .base import LabellerrProject from ..annotation_templates import LabellerrAnnotationTemplate from typing import List @@ -20,6 +21,7 @@ "LabellerrDocumentProject", "LabellerrImageProject", "LabellerrVideoProject", + "LabellerrTextProject", ] diff --git a/labellerr/core/projects/text_project.py b/labellerr/core/projects/text_project.py new file mode 100644 index 0000000..904f136 --- /dev/null +++ b/labellerr/core/projects/text_project.py @@ -0,0 +1,9 @@ +from .base import LabellerrProject, LabellerrProjectMeta + + +class TextProject(LabellerrProject): + + pass + + +LabellerrProjectMeta._register("text", TextProject)