From b6e6317cc92e657704b386c9eb960c51ca689b04 Mon Sep 17 00:00:00 2001 From: Narek Mkhitaryan Date: Thu, 27 Apr 2023 15:57:18 +0400 Subject: [PATCH 1/2] changes in entities to backend response independent --- requirements_extra.txt | 2 +- .../lib/core/entities/__init__.py | 2 -- .../lib/core/entities/classes.py | 6 ++--- src/superannotate/lib/core/entities/folder.py | 2 +- .../lib/core/entities/project.py | 17 +++++++------- .../lib/core/entities/project_entities.py | 18 --------------- tests/__init__.py | 14 +++++++----- .../folders/test_get_folder_metadata.py | 10 +++++++++ .../items/test_get_item_metadata.py | 22 +++++++++++++++---- .../projects/test_get_project_metadata.py | 19 ++++++++++++++++ 10 files changed, 69 insertions(+), 43 deletions(-) diff --git a/requirements_extra.txt b/requirements_extra.txt index 5fa8c52da..3073271a4 100644 --- a/requirements_extra.txt +++ b/requirements_extra.txt @@ -1,4 +1,4 @@ -Sphinx==6.1.3 +Sphinx==6.2.1 Jinja2==3.1.2 tox==4.4.5 sphinx_rtd_theme==1.2.0 diff --git a/src/superannotate/lib/core/entities/__init__.py b/src/superannotate/lib/core/entities/__init__.py index cc81ba8a7..84d280b5c 100644 --- a/src/superannotate/lib/core/entities/__init__.py +++ b/src/superannotate/lib/core/entities/__init__.py @@ -19,7 +19,6 @@ from lib.core.entities.project import UserEntity from lib.core.entities.project import WorkflowEntity from lib.core.entities.project_entities import BaseEntity -from lib.core.entities.project_entities import ImageInfoEntity from lib.core.entities.project_entities import S3FileEntity __all__ = [ @@ -44,7 +43,6 @@ "ConfigEntity", "WorkflowEntity", "FolderEntity", - "ImageInfoEntity", "S3FileEntity", "AnnotationClassEntity", "TeamEntity", diff --git a/src/superannotate/lib/core/entities/classes.py b/src/superannotate/lib/core/entities/classes.py index a584d2ffc..fa5cda8f1 100644 --- a/src/superannotate/lib/core/entities/classes.py +++ b/src/superannotate/lib/core/entities/classes.py @@ -62,7 +62,7 @@ class Attribute(TimedBaseModel): name: Optional[StrictStr] class Config: - extra = Extra.allow + extra = Extra.ignore def __hash__(self): return hash(f"{self.id}{self.group_id}{self.name}") @@ -78,7 +78,7 @@ class AttributeGroup(TimedBaseModel): default_value: Any class Config: - extra = Extra.allow + extra = Extra.ignore use_enum_values = True def __hash__(self): @@ -97,7 +97,7 @@ def __hash__(self): return hash(f"{self.id}{self.type}{self.name}") class Config: - extra = Extra.allow + extra = Extra.ignore json_encoders = { HexColor: lambda v: v.__root__, BaseTitledEnum: lambda v: v.value, diff --git a/src/superannotate/lib/core/entities/folder.py b/src/superannotate/lib/core/entities/folder.py index a1c0ebe3c..8b25c66f1 100644 --- a/src/superannotate/lib/core/entities/folder.py +++ b/src/superannotate/lib/core/entities/folder.py @@ -12,7 +12,7 @@ class FolderEntity(TimedBaseModel): status: Optional[FolderStatus] project_id: Optional[int] team_id: Optional[int] - is_root: Optional[bool] = (False,) + is_root: Optional[bool] = False folder_users: Optional[List[dict]] completedCount: Optional[int] diff --git a/src/superannotate/lib/core/entities/project.py b/src/superannotate/lib/core/entities/project.py index 60a646b8b..86cc2f74b 100644 --- a/src/superannotate/lib/core/entities/project.py +++ b/src/superannotate/lib/core/entities/project.py @@ -33,12 +33,8 @@ def validate(cls, v: datetime): class TimedBaseModel(BaseModel): - createdAt: Optional[StringDate] = Field( - None, alias="createdAt", description="Date of creation" - ) - updatedAt: Optional[StringDate] = Field( - None, alias="updatedAt", description="Update date" - ) + createdAt: Optional[StringDate] = None + updatedAt: Optional[StringDate] = None class AttachmentEntity(BaseModel): @@ -59,7 +55,10 @@ class WorkflowEntity(BaseModel): className: Optional[str] step: Optional[int] tool: Optional[int] - attribute: List = (tuple(),) + attribute: List = tuple() + + class Config: + extra = Extra.ignore def __copy__(self): return WorkflowEntity(step=self.step, tool=self.tool, attribute=self.attribute) @@ -91,8 +90,8 @@ class Config: class ProjectEntity(TimedBaseModel): id: Optional[int] team_id: Optional[int] - name: Optional[str] - type: Optional[ProjectType] + name: str + type: ProjectType description: Optional[str] instructions_link: Optional[str] creator_id: Optional[str] diff --git a/src/superannotate/lib/core/entities/project_entities.py b/src/superannotate/lib/core/entities/project_entities.py index f11dadf89..971431721 100644 --- a/src/superannotate/lib/core/entities/project_entities.py +++ b/src/superannotate/lib/core/entities/project_entities.py @@ -24,24 +24,6 @@ def to_dict(self): raise NotImplementedError -class ImageInfoEntity(BaseEntity): - def __init__( - self, - uuid=None, - width: float = None, - height: float = None, - ): - super().__init__(uuid), - self.width = width - self.height = height - - def to_dict(self): - return { - "width": self.width, - "height": self.height, - } - - class S3FileEntity(BaseEntity): def __init__(self, uuid, data, metadata: dict = None): super().__init__(uuid) diff --git a/tests/__init__.py b/tests/__init__.py index 09837d17a..11b7563ff 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -8,9 +8,13 @@ DATA_SET_PATH = Path(__file__).parent / "data_set" sys.path.insert(0, str(LIB_PATH)) -__all__ = ["DATA_SET_PATH"] -# from src.superannotate.lib.core import setup_logging -# -# logger = get_default_logger() -# logger.setLevel("DEBUG") +def compare_result(result: dict, expected: dict, ignore_keys: set = None): + for key in result: + if ignore_keys and key in ignore_keys: + continue + assert result[key] == expected[key] + return True + + +__all__ = ["DATA_SET_PATH", "compare_result"] diff --git a/tests/integration/folders/test_get_folder_metadata.py b/tests/integration/folders/test_get_folder_metadata.py index 08c683029..87c165489 100644 --- a/tests/integration/folders/test_get_folder_metadata.py +++ b/tests/integration/folders/test_get_folder_metadata.py @@ -1,5 +1,6 @@ from src.superannotate import AppException from src.superannotate import SAClient +from tests import compare_result from tests.integration.base import BaseTestCase from tests.integration.folders import FOLDER_KEYS @@ -12,6 +13,12 @@ class TestGetFolderMetadata(BaseTestCase): PROJECT_TYPE = "Vector" SPECIAL_CHARS = r"/\:*?“<>|" TEST_FOLDER_NAME = "folder_" + IGNORE_KEYS = {"id", "team_id", "createdAt", "updatedAt", "project_id"} + EXPECTED_FOLDER_METADATA = { + "folder_users": None, + "name": "folder_", + "status": "NotStarted", + } def test_get_folder_metadata(self): sa.create_folder(self.PROJECT_NAME, self.TEST_FOLDER_NAME) @@ -20,6 +27,9 @@ def test_get_folder_metadata(self): ) assert "is_root" not in folder_metadata self.assertListEqual(list(folder_metadata.keys()), FOLDER_KEYS) + assert compare_result( + folder_metadata, self.EXPECTED_FOLDER_METADATA, self.IGNORE_KEYS + ) # get not exiting folder with self.assertRaises(AppException) as cm: diff --git a/tests/integration/items/test_get_item_metadata.py b/tests/integration/items/test_get_item_metadata.py index 6572de6dc..f20f648af 100644 --- a/tests/integration/items/test_get_item_metadata.py +++ b/tests/integration/items/test_get_item_metadata.py @@ -2,6 +2,7 @@ from pathlib import Path from src.superannotate import SAClient +from tests import compare_result from tests.integration.base import BaseTestCase sa = SAClient() @@ -15,6 +16,20 @@ class TestGetEntityMetadataVector(BaseTestCase): CSV_PATH = "data_set/attach_urls.csv" IMAGE_NAME = "example_image_1.jpg" ATTACHED_IMAGE_NAME = "6022a74d5384c50017c366b3" + IGNORE_KEYS = {"id", "createdAt", "updatedAt"} + EXPECTED_ITEM_METADATA = { + "name": "example_image_1.jpg", + "path": "TestGetEntityMetadataVector", + "url": None, + "annotator_email": None, + "qa_email": None, + "annotation_status": "InProgress", + "entropy_value": None, + "prediction_status": "NotStarted", + "segmentation_status": None, + "approval_status": None, + "is_pinned": False, + } @property def folder_path(self): @@ -30,10 +45,9 @@ def test_get_item_metadata(self): ) item_metadata = sa.get_item_metadata(self.PROJECT_NAME, self.IMAGE_NAME) assert item_metadata["path"] == f"{self.PROJECT_NAME}" - assert item_metadata["prediction_status"] == "NotStarted" - assert item_metadata["segmentation_status"] is None - assert item_metadata["annotation_status"] == "InProgress" - assert item_metadata["approval_status"] is None + assert compare_result( + item_metadata, self.EXPECTED_ITEM_METADATA, self.IGNORE_KEYS + ) class TestGetEntityMetadataPixel(BaseTestCase): diff --git a/tests/integration/projects/test_get_project_metadata.py b/tests/integration/projects/test_get_project_metadata.py index ee82d7424..b403b74b7 100644 --- a/tests/integration/projects/test_get_project_metadata.py +++ b/tests/integration/projects/test_get_project_metadata.py @@ -1,4 +1,5 @@ from src.superannotate import SAClient +from tests import compare_result from tests.integration.base import BaseTestCase sa = SAClient() @@ -8,6 +9,21 @@ class TestGetProjectMetadata(BaseTestCase): PROJECT_NAME = "TestGetProjectMetadata" PROJECT_TYPE = "Vector" PROJECT_DESCRIPTION = "DESCRIPTION" + IGNORE_KEYS = {"id", "creator_id", "team_id", "createdAt", "updatedAt"} + EXPECTED_PROJECT_METADATA = { + "name": "TestGetProjectMetadata", + "type": "Vector", + "description": "DESCRIPTION", + "instructions_link": None, + "entropy_status": 1, + "sharing_status": None, + "status": "NotStarted", + "folder_id": None, + "upload_state": "EXTERNAL", + "users": [], + "completed_items_count": None, + "root_folder_completed_items_count": None, + } def test_metadata_payload(self): """ @@ -23,3 +39,6 @@ def test_metadata_payload(self): assert project["item_count"] == 10 projects = sa.search_projects(name=self.PROJECT_NAME, return_metadata=True) assert "item_count" not in projects[0] + assert compare_result( + projects[0], self.EXPECTED_PROJECT_METADATA, self.IGNORE_KEYS + ) From 5d79b221ca2ffecc23cc066cd9eef396bce3ca13 Mon Sep 17 00:00:00 2001 From: Narek Mkhitaryan Date: Tue, 2 May 2023 19:37:19 +0400 Subject: [PATCH 2/2] changes in class entity logic --- src/superannotate/lib/app/interface/sdk_interface.py | 5 +---- src/superannotate/lib/core/entities/classes.py | 1 - src/superannotate/lib/core/usecases/classes.py | 6 +++++- tests/integration/classes/test_create_annotation_class.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/superannotate/lib/app/interface/sdk_interface.py b/src/superannotate/lib/app/interface/sdk_interface.py index e0632848a..1d0886a2a 100644 --- a/src/superannotate/lib/app/interface/sdk_interface.py +++ b/src/superannotate/lib/app/interface/sdk_interface.py @@ -730,10 +730,7 @@ def search_annotation_classes( response = self.controller.annotation_classes.list(condition) if response.errors: raise AppException(response.errors) - return [ - i.dict(exclude={"attribute_groups": {"__all__": {"is_multiselect"}}}) - for i in response.data - ] + return response.data def set_project_status(self, project: NotEmptyStr, status: PROJECT_STATUS): """Set project status diff --git a/src/superannotate/lib/core/entities/classes.py b/src/superannotate/lib/core/entities/classes.py index fa5cda8f1..34686971e 100644 --- a/src/superannotate/lib/core/entities/classes.py +++ b/src/superannotate/lib/core/entities/classes.py @@ -73,7 +73,6 @@ class AttributeGroup(TimedBaseModel): group_type: Optional[GroupTypeEnum] class_id: Optional[StrictInt] name: Optional[StrictStr] - is_multiselect: Optional[bool] attributes: Optional[List[Attribute]] default_value: Any diff --git a/src/superannotate/lib/core/usecases/classes.py b/src/superannotate/lib/core/usecases/classes.py index 832852a3d..3049f1763 100644 --- a/src/superannotate/lib/core/usecases/classes.py +++ b/src/superannotate/lib/core/usecases/classes.py @@ -28,7 +28,11 @@ def __init__( def execute(self): response = self._service_provider.annotation_classes.list(self._condition) if response.ok: - self._response.data = response.data + classes = [ + entity.dict(by_alias=True, exclude_unset=True) + for entity in response.data + ] + self._response.data = classes else: self._response.errors = response.error return self._response diff --git a/tests/integration/classes/test_create_annotation_class.py b/tests/integration/classes/test_create_annotation_class.py index 52e344be8..a780f418d 100644 --- a/tests/integration/classes/test_create_annotation_class.py +++ b/tests/integration/classes/test_create_annotation_class.py @@ -64,7 +64,7 @@ def test_multi_select_to_checklist(self): attribute_groups=[ { "name": "test", - "is_multiselect": 1, + "group_type": "checklist", "attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}], } ],