diff --git a/src/superannotate/lib/app/interface/sdk_interface.py b/src/superannotate/lib/app/interface/sdk_interface.py index 3a7f8c05a..7ebd93462 100644 --- a/src/superannotate/lib/app/interface/sdk_interface.py +++ b/src/superannotate/lib/app/interface/sdk_interface.py @@ -38,6 +38,7 @@ from lib.app.serializers import ProjectSerializer from lib.app.serializers import SettingsSerializer from lib.app.serializers import TeamSerializer +from lib.app.serializers import ItemSerializer from lib.core import entities from lib.core import LIMITED_FUNCTIONS from lib.core.conditions import Condition @@ -85,6 +86,49 @@ def __init__( ): super().__init__(token, config_path) + def get_folder_by_id(self, project_id: int, folder_id: int): + """Returns the folder + :param folder_id: the id of the folder + :param project_id: the id of the project + :return: folder information + :rtype: dict + """ + + response = self.controller.get_folder_by_id( + folder_id=folder_id, + project_id=project_id + ) + + return FolderSerializer(response).serialize(exclude={"completedCount", "is_root"}) + + def get_project_by_id(self, project_id: int): + """Returns the project + :param project_id: the id of the project + :return: folder information + :rtype: dict + """ + response = self.controller.get_project_by_id( + project_id=project_id + ) + + return ProjectSerializer(response.data).serialize() + + def get_item_by_id(self, project_id: int, item_id: int): + """Returns the project + :param item_id: the id of the item + :param project_id: the id of the project + :return: folder information + :rtype: dict + """ + + response = self.controller.get_item_by_id( + item_id=item_id, + project_id=project_id + ) + + return ItemSerializer(response).serialize(exclude = {"url", "meta"}) + + def get_team_metadata(self): """Returns team metadata @@ -455,7 +499,7 @@ def get_project_metadata( include_settings: Optional[StrictBool] = False, include_workflow: Optional[StrictBool] = False, include_contributors: Optional[StrictBool] = False, - include_complete_image_count: Optional[StrictBool] = False, + include_complete_image_count: Optional[StrictBool] = False ): """Returns project metadata diff --git a/src/superannotate/lib/app/serializers.py b/src/superannotate/lib/app/serializers.py index eddd1958f..53c7d8fa3 100644 --- a/src/superannotate/lib/app/serializers.py +++ b/src/superannotate/lib/app/serializers.py @@ -165,6 +165,17 @@ def serialize(self): self.data["value"] = constance.ImageQuality.get_name(self.data["value"]) return self.data +class ItemSerializer(BaseSerializer): + def serialize( + self, + fields: List[str] = None, + by_alias: bool = False, + flat: bool = False, + exclude: Set[str] = None, + ): + data = super().serialize(fields, by_alias, flat, exclude) + + return data class EntitySerializer: @classmethod diff --git a/src/superannotate/lib/core/entities/__init__.py b/src/superannotate/lib/core/entities/__init__.py index 84c94a21d..668a7eb36 100644 --- a/src/superannotate/lib/core/entities/__init__.py +++ b/src/superannotate/lib/core/entities/__init__.py @@ -6,6 +6,9 @@ from lib.core.entities.items import DocumentEntity from lib.core.entities.items import ImageEntity from lib.core.entities.items import VideoEntity +from lib.core.entities.items import ClassificationEntity +from lib.core.entities.items import TiledEntity +from lib.core.entities.items import PointCloudEntity from lib.core.entities.project import AttachmentEntity from lib.core.entities.project import MLModelEntity from lib.core.entities.project import ProjectEntity diff --git a/src/superannotate/lib/core/entities/items.py b/src/superannotate/lib/core/entities/items.py index bda7c6dce..f3e0090fb 100644 --- a/src/superannotate/lib/core/entities/items.py +++ b/src/superannotate/lib/core/entities/items.py @@ -28,7 +28,18 @@ class VideoEntity(BaseItemEntity): class Config: extra = Extra.ignore - class DocumentEntity(BaseItemEntity): class Config: extra = Extra.ignore + +class TiledEntity(BaseItemEntity): + class Config: + extra = Extra.ignore + +class ClassificationEntity(BaseItemEntity): + class Config: + extra = Extra.ignore + +class PointCloudEntity(BaseItemEntity): + class Config: + extra = Extra.ignore diff --git a/src/superannotate/lib/core/service_types.py b/src/superannotate/lib/core/service_types.py index 5f5af51a1..e0a305407 100644 --- a/src/superannotate/lib/core/service_types.py +++ b/src/superannotate/lib/core/service_types.py @@ -164,10 +164,27 @@ def set_error(self, value: Union[dict, str]): self._error = value +class ImageResponse(ServiceResponse): + data: entities.ImageEntity = None + +class VideoResponse(ServiceResponse): + data: entities.VideoEntity = None + +class DocumentResponse(ServiceResponse): + data: entities.DocumentEntity = None + +class TiledResponse(ServiceResponse): + data: entities.TiledEntity = None + +class ClassificationResponse(ServiceResponse): + data: entities.ClassificationEntity = None + +class PointCloudResponse(ServiceResponse): + data: entities.PointCloudEntity = None + class TeamResponse(ServiceResponse): data: entities.TeamEntity = None - class ModelListResponse(ServiceResponse): data: List[entities.AnnotationClassEntity] = None diff --git a/src/superannotate/lib/core/serviceproviders.py b/src/superannotate/lib/core/serviceproviders.py index 6df95690b..f7ac2ce80 100644 --- a/src/superannotate/lib/core/serviceproviders.py +++ b/src/superannotate/lib/core/serviceproviders.py @@ -155,6 +155,10 @@ def upload_priority_scores( class BaseFolderService(SuperannotateServiceProvider): + @abstractmethod + def get_by_id(self, folder_id: int, project_id: int, team_id: int): + raise NotImplementedError + @abstractmethod def get_by_name(self, project: entities.ProjectEntity, name: str) -> FolderResponse: raise NotImplementedError diff --git a/src/superannotate/lib/core/usecases/folders.py b/src/superannotate/lib/core/usecases/folders.py index a900ed6a3..321cb131f 100644 --- a/src/superannotate/lib/core/usecases/folders.py +++ b/src/superannotate/lib/core/usecases/folders.py @@ -14,6 +14,32 @@ logger = get_default_logger() +class GetFolderByIDUseCase(BaseUseCase): + def __init__(self, project_id, folder_id, team_id, service_provider): + self._project_id = project_id + self._folder_id = folder_id + self._team_id = team_id + self._service_provider = service_provider + super().__init__() + + def execute(self): + try: + response = self._service_provider.folders.get_by_id( + folder_id = self._folder_id, + project_id = self._project_id, + team_id = self._team_id + ) + except AppException as e: + self._response.errors=e + else: + self._response.data = response.data + + if not response.ok: + self._response.errors = AppException(response.error) + + return self._response + + class CreateFolderUseCase(BaseUseCase): def __init__( self, diff --git a/src/superannotate/lib/core/usecases/items.py b/src/superannotate/lib/core/usecases/items.py index 4423cdcc1..4c0998bb9 100644 --- a/src/superannotate/lib/core/usecases/items.py +++ b/src/superannotate/lib/core/usecases/items.py @@ -34,6 +34,31 @@ logger = get_default_logger() +class GetItemByIDUseCase(BaseUseCase): + def __init__(self, item_id, project, service_provider): + self._item_id = item_id + self._project = project + self._service_provider = service_provider + super().__init__() + + def execute(self, ): + + try: + response = self._service_provider.items.get_by_id( + item_id = self._item_id, + project_id = self._project.id, + project_type = self._project.type + + ) + except AppException as e: + self._response.errors = e + else: + self._response.data = response.data + + if not response.ok: + self._response.errors = response.error + + return self._response class GetItem(BaseReportableUseCase): def __init__( diff --git a/src/superannotate/lib/core/usecases/projects.py b/src/superannotate/lib/core/usecases/projects.py index 7bf9bacd3..ac5615bca 100644 --- a/src/superannotate/lib/core/usecases/projects.py +++ b/src/superannotate/lib/core/usecases/projects.py @@ -24,6 +24,26 @@ logger = get_default_logger() +class GetProjectByIDUseCase(BaseUseCase): + def __init__(self, project_id, service_provider): + self._project_id = project_id + self._service_provider=service_provider + super().__init__() + + def execute(self): + try: + + self._response.data= self._service_provider.projects.get_by_id( + project_id = self._project_id + ) + + except AppException as e: + self._response.errors = e + else: + if not self._response.data.data: + self._response.errors = AppException("Either the specified project does not exist or you do not have permission to view it") + + return self._response class GetProjectsUseCase(BaseUseCase): def __init__( diff --git a/src/superannotate/lib/infrastructure/controller.py b/src/superannotate/lib/infrastructure/controller.py index 004a29763..c9c968e8d 100644 --- a/src/superannotate/lib/infrastructure/controller.py +++ b/src/superannotate/lib/infrastructure/controller.py @@ -47,6 +47,14 @@ def __init__(self, service_provider: ServiceProvider): class ProjectManager(BaseManager): + def get_by_id(self, project_id): + use_case = usecases.GetProjectByIDUseCase( + project_id=project_id, + service_provider=self.service_provider + ) + response = use_case.execute() + return response + def get_by_name(self, name: str): use_case = usecases.GetProjectByNameUseCase( name=name, service_provider=self.service_provider @@ -302,6 +310,16 @@ def create(self, project: ProjectEntity, folder: FolderEntity): ) return use_case.execute() + def get_by_id(self, folder_id, project_id, team_id): + use_case = usecases.GetFolderByIDUseCase( + folder_id=folder_id, + project_id=project_id, + team_id=team_id, + service_provider = self.service_provider + ) + result = use_case.execute() + return result + def list(self, project: ProjectEntity, condition: Condition = None): use_case = usecases.SearchFoldersUseCase( project=project, service_provider=self.service_provider, condition=condition @@ -355,6 +373,15 @@ def get_by_name( ) return use_case.execute() + def get_by_id(self, item_id: int, project : ProjectEntity): + use_case = usecases.GetItemByIDUseCase( + item_id = item_id, + project = project.data, + service_provider = self.service_provider + ) + return use_case.execute() + + def list( self, project: ProjectEntity, @@ -832,6 +859,46 @@ def set_default(cls, obj): cls.DEFAULT = obj return cls.DEFAULT + def get_folder_by_id( + self, folder_id: int, project_id: int + ) -> FolderEntity: + response= self.folders.get_by_id( + folder_id = folder_id, + project_id = project_id, + team_id = self.team_id + ) + + if response.errors: + raise AppException(response.errors) + + return response.data + + def get_project_by_id( + self, project_id: int + )->ProjectEntity: + response = self.projects.get_by_id( + project_id = project_id + ) + if response.errors: + raise AppException(response.errors) + + return response.data + + def get_item_by_id( + self, item_id: int, project_id:int + ): + project=self.get_project_by_id(project_id = project_id) + response = self.items.get_by_id( + item_id = item_id, + project = project + ) + + if response.errors: + raise AppException(response.errors) + + return response.data + + def get_project_folder_by_path( self, path: Union[str, Path] ) -> Tuple[ProjectEntity, FolderEntity]: diff --git a/src/superannotate/lib/infrastructure/services/folder.py b/src/superannotate/lib/infrastructure/services/folder.py index f4c7ad7f7..e29c65118 100644 --- a/src/superannotate/lib/infrastructure/services/folder.py +++ b/src/superannotate/lib/infrastructure/services/folder.py @@ -13,7 +13,19 @@ class FolderService(BaseFolderService): URL_GET_BY_NAME = "folder/getFolderByName" URL_DELETE_MULTIPLE = "image/delete/images" URL_ASSIGN_FOLDER = "folder/editAssignment" + URL_GET_BY_ID = "folder/getFolderById" + def get_by_id(self, folder_id, project_id, team_id): + params = { + "team_id": team_id, + "folder_id": folder_id, + "project_id": project_id + } + response = self.client.request( + self.URL_GET_BY_ID, "get", params = params, content_type=FolderResponse + ) + + return response def get_by_name(self, project: entities.ProjectEntity, name: str): params = {"project_id": project.id, "name": name} return self.client.request( diff --git a/src/superannotate/lib/infrastructure/services/item.py b/src/superannotate/lib/infrastructure/services/item.py index 55b11cfc5..ac52641ea 100644 --- a/src/superannotate/lib/infrastructure/services/item.py +++ b/src/superannotate/lib/infrastructure/services/item.py @@ -7,9 +7,16 @@ from lib.core.exceptions import AppException from lib.core.exceptions import BackendError from lib.core.service_types import ItemListResponse +from lib.core.service_types import DocumentResponse +from lib.core.service_types import ImageResponse +from lib.core.service_types import ClassificationResponse +from lib.core.service_types import VideoResponse +from lib.core.service_types import PointCloudResponse +from lib.core.service_types import TiledResponse from lib.core.serviceproviders import BaseItemService from lib.core.types import Attachment from lib.core.types import AttachmentMeta +from lib.core.enums import ProjectType class ItemService(BaseItemService): @@ -22,6 +29,33 @@ class ItemService(BaseItemService): URL_COPY_PROGRESS = "images/copy-image-progress" URL_DELETE_ITEMS = "image/delete/images" URL_SET_ANNOTATION_STATUSES = "image/updateAnnotationStatusBulk" + URL_GET_BY_ID = "image/{image_id}" + + PROJECT_TYPE_RESPOSE_MAP = { + ProjectType.VECTOR : ImageResponse, + ProjectType.OTHER: ClassificationResponse, + ProjectType.VIDEO: VideoResponse, + ProjectType.TILED: TiledResponse, + ProjectType.PIXEL: ImageResponse, + ProjectType.POINT_CLOUD: PointCloudResponse + + } + + def get_by_id(self, item_id, project_id, project_type): + + params = { + "project_id": project_id + } + + content_type = self.PROJECT_TYPE_RESPOSE_MAP[project_type] + + response = self.client.request( + url=self.URL_GET_BY_ID.format(image_id=item_id), + params = params, + content_type=content_type + ) + + return response def list(self, condition: Condition = None): return self.client.paginate( diff --git a/src/superannotate/lib/infrastructure/services/project.py b/src/superannotate/lib/infrastructure/services/project.py index 7b30777a1..5e534d84c 100644 --- a/src/superannotate/lib/infrastructure/services/project.py +++ b/src/superannotate/lib/infrastructure/services/project.py @@ -19,6 +19,14 @@ class ProjectService(BaseProjectService): URL_WORKFLOW_ATTRIBUTE = "project/{}/workflow_attribute" URL_UPLOAD_PRIORITY_SCORES = "images/updateEntropy" URL_ASSIGN_ITEMS = "images/editAssignment/" + URL_GET_BY_ID = "project/{project_id}" + + def get_by_id(self, project_id: int): + params = {} + result = self.client.request( + self.URL_GET_BY_ID.format(project_id=project_id), "get", params=params, content_type=ProjectResponse + ) + return result def get(self, uuid: int): return self.client.request(