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
1 change: 1 addition & 0 deletions docs/source/superannotate.sdk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ________
.. automethod:: superannotate.SAClient.upload_images_to_project
.. automethod:: superannotate.SAClient.attach_items_from_integrated_storage
.. automethod:: superannotate.SAClient.upload_image_to_project
.. automethod:: superannotate.SAClient.upload_annotations
.. automethod:: superannotate.SAClient.delete_annotations
.. _ref_upload_images_from_folder_to_project:
.. automethod:: superannotate.SAClient.upload_images_from_folder_to_project
Expand Down
65 changes: 63 additions & 2 deletions src/superannotate/lib/app/interface/sdk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1308,11 +1308,12 @@ def delete_annotation_class(
try:
annotation_class = AnnotationClassEntity(
name=annotation_class,
color="#ffffff", #Random, just need to serialize
color="#ffffff", # Random, just need to serialize
)
except ValidationError as e:
raise AppException(wrap_error(e))

else:
annotation_class = AnnotationClassEntity(**annotation_class)
project = self.controller.projects.get_by_name(project).data

self.controller.annotation_classes.delete(
Expand Down Expand Up @@ -1491,12 +1492,52 @@ def download_image(
logger.info(f"Downloaded image {image_name} to {local_dir_path} ")
return response.data

def upload_annotations(
self, project: NotEmptyStr, annotations: List[dict], keep_status: bool = False
):
"""Uploads a list of annotation dicts as annotations to the SuperAnnotate directory.

:param project: project name or folder path (e.g., "project1/folder1")
:type project: str or dict

:param annotations: list of annotation dictionaries corresponding to SuperAnnotate format
:type annotations: list of dicts

:param keep_status: If False, the annotation status will be automatically
updated to "InProgress," otherwise the current status will be kept.
:type keep_status: bool


:return: a dictionary containing lists of successfully uploaded, failed and skipped name
:rtype: dict

Response Example:
::
{
"succeeded": [""],
"failed":[""],
"skipped": [""]
}

"""
project, folder = self.controller.get_project_folder_by_path(project)
response = self.controller.annotations.upload_multiple(
project=project,
folder=folder,
annotations=annotations,
keep_status=keep_status,
)
if response.errors:
raise AppException(response.errors)
return response.data

def upload_annotations_from_folder_to_project(
self,
project: Union[NotEmptyStr, dict],
folder_path: Union[str, Path],
from_s3_bucket=None,
recursive_subfolders: Optional[StrictBool] = False,
keep_status=False,
):
"""Finds and uploads all JSON files in the folder_path as annotations to the project.

Expand All @@ -1511,13 +1552,20 @@ def upload_annotations_from_folder_to_project(

:param project: project name or folder path (e.g., "project1/folder1")
:type project: str or dict

:param folder_path: from which folder to upload annotations
:type folder_path: str or dict

:param from_s3_bucket: AWS S3 bucket to use. If None then folder_path is in local filesystem
:type from_s3_bucket: str

:param recursive_subfolders: enable recursive subfolder parsing
:type recursive_subfolders: bool

:param keep_status: If False, the annotation status will be automatically
updated to "InProgress," otherwise the current status will be kept.
:type keep_status: bool

:return: paths to annotations uploaded, could-not-upload, missing-images
:rtype: tuple of list of strs
"""
Expand Down Expand Up @@ -1550,6 +1598,7 @@ def upload_annotations_from_folder_to_project(
annotation_paths=annotation_paths, # noqa: E203
client_s3_bucket=from_s3_bucket,
folder_path=folder_path,
keep_status=keep_status,
)
if response.errors:
raise AppException(response.errors)
Expand Down Expand Up @@ -1585,6 +1634,12 @@ def upload_preannotations_from_folder_to_project(
:return: paths to pre-annotations uploaded and could-not-upload
:rtype: tuple of list of strs
"""
warning_msg = (
"The SAClient.upload_preannotations_from_folder_to_project"
" method will be deprecated with the Superannotate Python SDK 4.4.6 release"
)
warnings.warn(warning_msg, DeprecationWarning)
logger.warning(warning_msg)
project_name, folder_name = extract_project_folder(project)
project_folder_name = project_name + (f"/{folder_name}" if folder_name else "")
project = self.controller.get_project(project_name)
Expand Down Expand Up @@ -1629,6 +1684,7 @@ def upload_image_annotations(
annotation_json: Union[str, Path, dict],
mask: Optional[Union[str, Path, bytes]] = None,
verbose: Optional[StrictBool] = True,
keep_status: bool = False,
):
"""Upload annotations from JSON (also mask for pixel annotations)
to the image.
Expand All @@ -1648,6 +1704,10 @@ def upload_image_annotations(
:param verbose: Turns on verbose output logging during the proces.
:type verbose: bool

:param keep_status: If False, the annotation status will be automatically
updated to "InProgress," otherwise the current status will be kept.
:type keep_status: bool

"""

project_name, folder_name = extract_project_folder(project)
Expand Down Expand Up @@ -1690,6 +1750,7 @@ def upload_image_annotations(
team=self.controller.team,
mask=mask,
verbose=verbose,
keep_status=keep_status,
)
if response.errors and not response.errors == constants.INVALID_JSON_MESSAGE:
raise AppException(response.errors)
Expand Down
2 changes: 1 addition & 1 deletion src/superannotate/lib/core/entities/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def __get_validators__(cls):

@classmethod
def validate(cls, v: datetime):
v = v.isoformat().split('+')[0]+'.000Z'
v = v.isoformat().split("+")[0] + ".000Z"
return v


Expand Down
1 change: 1 addition & 0 deletions src/superannotate/lib/core/service_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class ModelListResponse(ServiceResponse):
class IntegrationResponse(ServiceResponse):
data: List[entities.IntegrationEntity] = None


class AnnotationClassListResponse(ServiceResponse):
data: List[entities.AnnotationClassEntity] = None

Expand Down
Loading