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
4 changes: 2 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ History
All release highlights of this project will be documented in this file.

4.4.15 - August 20, 2023
_______________________
________________________

**Added**

- Support for `relationship` class types in the document project.


4.4.14 - August 20, 2023
_______________________
________________________

**Added**

Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
pydantic~=1.10
pydantic>=1.10,!=2.0.*
aiohttp~=3.8
boto3~=1.26
opencv-python~=4.7
opencv-python-headless~=4.7
packaging~=23.1
plotly~=5.14
email-validator~=2.0
pandas~=1.3
ffmpeg-python~=0.2
pillow~=9.5
pillow>=9.5,~=10.0
tqdm~=4.66.1
requests~=2.31.0
aiofiles==23.1.0
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def get_version():
description="Python SDK to SuperAnnotate platform",
license="MIT",
author="SuperAnnotate AI",
author_email="suppoort@superannotate.com",
author_email="support@superannotate.com",
url="https://github.com/superannotateai/superannotate-python-sdk",
long_description=open("README.rst").read(),
long_description_content_type="text/x-rst",
Expand Down
2 changes: 1 addition & 1 deletion src/superannotate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys


__version__ = "4.4.15"
__version__ = "4.4.16dev1"

sys.path.append(os.path.split(os.path.realpath(__file__))[0])

Expand Down
15 changes: 6 additions & 9 deletions src/superannotate/lib/app/interface/base_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
from typing import Sized

import lib.core as constants
import pydantic
from lib.app.interface.types import validate_arguments
from lib.core import CONFIG
from lib.core import setup_logging
from lib.core.entities.base import ConfigEntity
from lib.core.entities.base import TokenStr
from lib.core.exceptions import AppException
from lib.core.pydantic_v1 import ErrorWrapper
from lib.core.pydantic_v1 import ValidationError
from lib.infrastructure.controller import Controller
from lib.infrastructure.utils import extract_project_folder
from lib.infrastructure.validators import wrap_error
Expand Down Expand Up @@ -61,7 +62,7 @@ def __init__(self, token: TokenStr = None, config_path: str = None):
raise AppException(
f"SuperAnnotate config file {constants.CONFIG_INI_FILE_LOCATION} not found."
)
except pydantic.ValidationError as e:
except ValidationError as e:
raise AppException(wrap_error(e))
except KeyError:
raise
Expand All @@ -78,13 +79,9 @@ def _retrieve_configs_from_json(path: Path) -> typing.Union[ConfigEntity]:
token = json_data["token"]
try:
config = ConfigEntity(SA_TOKEN=token)
except pydantic.ValidationError:
raise pydantic.ValidationError(
[
pydantic.error_wrappers.ErrorWrapper(
ValueError("Invalid token."), loc="token"
)
],
except ValidationError:
raise ValidationError(
[ErrorWrapper(ValueError("Invalid token."), loc="token")],
model=ConfigEntity,
)
host = json_data.get("main_endpoint")
Expand Down
38 changes: 29 additions & 9 deletions src/superannotate/lib/app/interface/sdk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from typing import TypeVar
from typing import Union

import pydantic
from typing_extensions import Literal

if sys.version_info < (3, 11):
Expand All @@ -24,10 +23,7 @@
from typing import TypedDict, NotRequired, Required # noqa

import boto3
from pydantic import conlist
from pydantic import constr
from pydantic import parse_obj_as
from pydantic.error_wrappers import ValidationError

from tqdm import tqdm

import lib.core as constants
Expand Down Expand Up @@ -61,16 +57,18 @@
from lib.core.types import MLModel
from lib.core.types import PriorityScoreEntity
from lib.core.types import Project
from lib.core.pydantic_v1 import ValidationError
from lib.core.pydantic_v1 import constr
from lib.core.pydantic_v1 import conlist
from lib.core.pydantic_v1 import parse_obj_as
from lib.infrastructure.utils import extract_project_folder
from lib.infrastructure.validators import wrap_error


logger = logging.getLogger("sa")


NotEmptyStr = TypeVar("NotEmptyStr", bound=constr(strict=True, min_length=1))


PROJECT_STATUS = Literal["NotStarted", "InProgress", "Completed", "OnHold"]

PROJECT_TYPE = Literal[
Expand Down Expand Up @@ -1131,6 +1129,7 @@ def prepare_export(
annotation_statuses: Optional[List[ANNOTATION_STATUS]] = None,
include_fuse: Optional[bool] = False,
only_pinned=False,
**kwargs,
):
"""Prepare annotations and classes.json for export. Original and fused images for images with
annotations can be included with include_fuse flag.
Expand All @@ -1147,6 +1146,8 @@ def prepare_export(
:type include_fuse: bool
:param only_pinned: enable only pinned output in export. This option disables all other types of output.
:type only_pinned: bool
:param kwargs: Arbitrary kwarg ``integration_name``
can be provided which will be used as a storage to store export file

:return: metadata object of the prepared export
:rtype: dict
Expand All @@ -1165,12 +1166,22 @@ def prepare_export(
constants.AnnotationStatus.COMPLETED.name,
constants.AnnotationStatus.SKIPPED.name,
]
integration_name = kwargs.get("integration_name")
integration_id = None
if integration_name:
for integration in self.controller.integrations.list().data:
if integration.name == integration_name:
integration_id = integration.id
break
else:
raise AppException("Integration not found.")
response = self.controller.prepare_export(
project_name=project_name,
folder_names=folders,
include_fuse=include_fuse,
only_pinned=only_pinned,
annotation_statuses=annotation_statuses,
integration_id=integration_id,
)
if response.errors:
raise AppException(response.errors)
Expand Down Expand Up @@ -2095,7 +2106,7 @@ def delete_annotations(

:param project: project name or folder path (e.g., "project1/folder1")
:type project: str
:param item_names: item names. If None, all the items in the specified directory will be deleted.
:param item_names: item names. If None, all the annotations in the specified directory will be deleted.
:type item_names: list of strs
"""

Expand Down Expand Up @@ -2538,6 +2549,15 @@ def attach_items(

:return: uploaded, failed and duplicated item names
:rtype: tuple of list of strs

Example:
::
client = SAClient()
client.attach_items(
project = "Medical Annotations",
attachments = [{"name": "item", "url": "https://..."}]
)

"""

project_name, folder_name = extract_project_folder(project)
Expand All @@ -2549,7 +2569,7 @@ def attach_items(
for item, count in collections.Counter(attachments).items()
if count > 1
]
except pydantic.ValidationError:
except ValidationError:
(
unique_attachments,
duplicate_attachments,
Expand Down
14 changes: 7 additions & 7 deletions src/superannotate/lib/app/interface/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

from lib.core.enums import BaseTitledEnum
from lib.core.exceptions import AppException
from lib.core.pydantic_v1 import constr
from lib.core.pydantic_v1 import errors
from lib.core.pydantic_v1 import pydantic_validate_arguments
from lib.core.pydantic_v1 import PydanticTypeError
from lib.core.pydantic_v1 import StrictStr
from lib.core.pydantic_v1 import StrRegexError
from lib.core.pydantic_v1 import ValidationError
from lib.infrastructure.validators import wrap_error
from pydantic import constr
from pydantic import errors
from pydantic import StrictStr
from pydantic import validate_arguments as pydantic_validate_arguments
from pydantic import ValidationError
from pydantic.errors import PydanticTypeError
from pydantic.errors import StrRegexError


class EnumMemberError(PydanticTypeError):
Expand Down
2 changes: 1 addition & 1 deletion src/superannotate/lib/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from typing import Union

import superannotate.lib.core as constance
from pydantic import BaseModel
from superannotate.lib.core.entities import BaseEntity
from superannotate.lib.core.pydantic_v1 import BaseModel


class BaseSerializer(ABC):
Expand Down
24 changes: 12 additions & 12 deletions src/superannotate/lib/core/entities/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
from lib.core import LOG_FILE_LOCATION
from lib.core.enums import AnnotationStatus
from lib.core.enums import BaseTitledEnum
from pydantic import BaseModel as PydanticBaseModel
from pydantic import Extra
from pydantic import Field
from pydantic import StrictStr
from pydantic.datetime_parse import parse_datetime
from pydantic.typing import is_namedtuple
from pydantic.utils import ROOT_KEY
from pydantic.utils import sequence_like
from pydantic.utils import ValueItems
from typing_extensions import Literal
from lib.core.pydantic_v1 import BaseModel
from lib.core.pydantic_v1 import Extra
from lib.core.pydantic_v1 import Field
from lib.core.pydantic_v1 import is_namedtuple
from lib.core.pydantic_v1 import Literal
from lib.core.pydantic_v1 import parse_datetime
from lib.core.pydantic_v1 import ROOT_KEY
from lib.core.pydantic_v1 import sequence_like
from lib.core.pydantic_v1 import StrictStr
from lib.core.pydantic_v1 import ValueItems

DATE_TIME_FORMAT_ERROR_MESSAGE = (
"does not match expected format YYYY-MM-DDTHH:MM:SS.fffZ"
Expand All @@ -37,7 +37,7 @@
_missing = object()


class BaseModel(PydanticBaseModel):
class BaseModel(BaseModel):
"""
Added new extra keys
- use_enum_names: that's for BaseTitledEnum to use names instead of enum objects
Expand Down Expand Up @@ -121,7 +121,7 @@ def _get_value(
exclude_none: bool,
) -> Any:

if isinstance(v, PydanticBaseModel):
if isinstance(v, BaseModel):
v_dict = v.dict(
by_alias=by_alias,
exclude_unset=exclude_unset,
Expand Down
19 changes: 9 additions & 10 deletions src/superannotate/lib/core/entities/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,24 @@
from typing import Optional

from lib.core.entities.base import BaseModel
from lib.core.entities.base import parse_datetime
from lib.core.enums import BaseTitledEnum
from lib.core.enums import ClassTypeEnum
from pydantic import BaseModel as BasePydanticModel
from pydantic import Extra
from pydantic import Field
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import validator
from pydantic.color import Color
from pydantic.color import ColorType
from pydantic.datetime_parse import parse_datetime
from lib.core.pydantic_v1 import Color
from lib.core.pydantic_v1 import ColorType
from lib.core.pydantic_v1 import Extra
from lib.core.pydantic_v1 import Field
from lib.core.pydantic_v1 import StrictInt
from lib.core.pydantic_v1 import StrictStr
from lib.core.pydantic_v1 import validator

DATE_REGEX = r"\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d{3})Z"
DATE_TIME_FORMAT_ERROR_MESSAGE = (
"does not match expected format YYYY-MM-DDTHH:MM:SS.fffZ"
)


class HexColor(BasePydanticModel):
class HexColor(BaseModel):
__root__: ColorType

@validator("__root__", pre=True)
Expand Down
2 changes: 1 addition & 1 deletion src/superannotate/lib/core/entities/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from lib.core.entities.base import TimedBaseModel
from lib.core.enums import FolderStatus
from pydantic import Extra
from lib.core.pydantic_v1 import Extra


class FolderEntity(TimedBaseModel):
Expand Down
9 changes: 7 additions & 2 deletions src/superannotate/lib/core/entities/integrations.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from lib.core.entities.base import TimedBaseModel
from lib.core.enums import IntegrationTypeEnum
from pydantic import Extra
from pydantic import Field

try:
from pydantic.v1 import Extra
from pydantic.v1 import Field
except ImportError:
from pydantic import Extra
from pydantic import Field


class IntegrationEntity(TimedBaseModel):
Expand Down
4 changes: 2 additions & 2 deletions src/superannotate/lib/core/entities/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from lib.core.entities.base import BaseItemEntity
from lib.core.enums import ApprovalStatus
from lib.core.enums import SegmentationStatus
from pydantic import Extra
from pydantic import Field
from lib.core.pydantic_v1 import Extra
from lib.core.pydantic_v1 import Field


class ImageEntity(BaseItemEntity):
Expand Down
14 changes: 7 additions & 7 deletions src/superannotate/lib/core/entities/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
from lib.core.enums import ProjectStatus
from lib.core.enums import ProjectType
from lib.core.enums import UserRole
from pydantic import Extra
from pydantic import Field
from pydantic import StrictBool
from pydantic import StrictFloat
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic.datetime_parse import parse_datetime
from lib.core.pydantic_v1 import Extra
from lib.core.pydantic_v1 import Field
from lib.core.pydantic_v1 import parse_datetime
from lib.core.pydantic_v1 import StrictBool
from lib.core.pydantic_v1 import StrictFloat
from lib.core.pydantic_v1 import StrictInt
from lib.core.pydantic_v1 import StrictStr


class StringDate(datetime.datetime):
Expand Down
4 changes: 2 additions & 2 deletions src/superannotate/lib/core/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def generate_thumb(self):

thumbnail_size = (128, 96)
background = Image.new("RGB", thumbnail_size, "black")
image.thumbnail(thumbnail_size, Image.ANTIALIAS)
image.thumbnail(thumbnail_size, Image.LANCZOS)
(w, h) = image.size
background.paste(
image, ((thumbnail_size[0] - w) // 2, (thumbnail_size[1] - h) // 2)
Expand All @@ -99,7 +99,7 @@ def generate_huge(self, base_width: int = 600) -> Tuple[io.BytesIO, float, float
width, height = im.size
buffer = io.BytesIO()
h_size = int(height * base_width / width)
im.resize((base_width, h_size), Image.ANTIALIAS).convert("RGB").save(
im.resize((base_width, h_size), Image.LANCZOS).convert("RGB").save(
buffer, "JPEG"
)
buffer.seek(0)
Expand Down
Loading