Skip to content

Commit 5a913ab

Browse files
authored
Merge pull request #705 from superannotateai/FRIDAY-2925
changed classes interface to sdk_core
2 parents ab94ddf + b760c2f commit 5a913ab

File tree

10 files changed

+204
-442
lines changed

10 files changed

+204
-442
lines changed

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 88 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,10 @@
6060
from lib.core.types import MLModel
6161
from lib.core.types import PriorityScoreEntity
6262

63-
from lib.core.pydantic_v1 import ValidationError
6463
from lib.core.pydantic_v1 import constr
6564
from lib.core.pydantic_v1 import conlist
6665
from lib.core.pydantic_v1 import parse_obj_as
6766
from lib.infrastructure.utils import extract_project_folder
68-
from lib.infrastructure.validators import wrap_error
6967

7068
from superannotate_core.core.entities import BaseItemEntity
7169
from superannotate_core.app import Project, Folder
@@ -743,7 +741,9 @@ def get_project_workflow(self, project: Union[str, dict]):
743741
return workflow.data
744742

745743
def search_annotation_classes(
746-
self, project: Union[NotEmptyStr, dict], name_contains: Optional[str] = None
744+
self,
745+
project: Union[NotEmptyStr, dict, Project],
746+
name_contains: Optional[str] = None,
747747
):
748748
"""Searches annotation classes by name_prefix (case-insensitive)
749749
@@ -757,17 +757,20 @@ def search_annotation_classes(
757757
:return: annotation classes of the project
758758
:rtype: list of dicts
759759
"""
760-
project_name, folder_name = extract_project_folder(project)
760+
if isinstance(project, Project):
761+
project = project.dict()
762+
763+
project_name, _ = extract_project_folder(project)
761764
project = self.controller.get_project(project_name)
762-
condition = Condition("project_id", project.id, EQ)
763-
if name_contains:
764-
condition &= Condition("name", name_contains, EQ) & Condition(
765-
"pattern", True, EQ
766-
)
767-
response = self.controller.annotation_classes.list(condition)
768-
if response.errors:
769-
raise AppException(response.errors)
770-
return response.data
765+
condition = (
766+
Condition("name", name_contains, EQ) & Condition("pattern", True, EQ)
767+
if name_contains
768+
else None
769+
)
770+
return [
771+
annotation_class.dict()
772+
for annotation_class in project.list_annotation_classes(condition)
773+
]
771774

772775
def set_project_status(self, project: NotEmptyStr, status: PROJECT_STATUS):
773776
"""Set project status
@@ -1353,8 +1356,7 @@ def upload_video_to_project(
13531356

13541357
def create_annotation_class(
13551358
self,
1356-
# todo project: Union[Project, NotEmptyStr],
1357-
project: Union[NotEmptyStr],
1359+
project: Union[NotEmptyStr, dict, Project],
13581360
name: NotEmptyStr,
13591361
color: NotEmptyStr,
13601362
attribute_groups: Optional[List[AttributeGroupSchema]] = None,
@@ -1447,38 +1449,41 @@ def create_annotation_class(
14471449
)
14481450
14491451
"""
1450-
# todo if isinstance(project, Project):
1451-
# if isinstance(project, Project):
1452-
# project = project.dict()
1453-
_class_type = ClassTypeEnum(class_type)
1454-
# try:
1455-
# annotation_class = AnnotationClass(
1456-
# {
1457-
# "name": name,
1458-
# "color": color,
1459-
# "type": ClassTypeEnum(class_type),
1460-
# "attribute_groups": attribute_groups,
1461-
# }
1462-
# )
1463-
# except ValidationError as e:
1464-
# raise AppException(wrap_error(e))
1465-
project = self.controller.get_project(project)
1452+
if isinstance(project, Project):
1453+
project = project.dict()
1454+
1455+
project_name, _ = extract_project_folder(project)
1456+
project = self.controller.get_project(project_name)
1457+
1458+
if project.type == ProjectType.Pixel.value and any(
1459+
"default_value" in attr_group.keys() for attr_group in attribute_groups
1460+
):
1461+
raise AppException(
1462+
'The "default_value" key is not supported for project type Pixel.'
1463+
)
1464+
1465+
_class_type = ClassTypeEnum.get_value(class_type)
14661466
if (
14671467
project.type != ProjectType.Document
1468-
and _class_type == ClassTypeEnum.RELATIONSHIP
1468+
and _class_type == ClassTypeEnum.relationship
14691469
):
14701470
raise AppException(
14711471
f"{class_type} class type is not supported in {project.type.name} project."
14721472
)
1473-
project.create_annotation_class(
1473+
annotation_class = project.create_annotation_class(
14741474
name=name,
14751475
color=color,
14761476
class_type=_class_type,
14771477
attribute_groups=attribute_groups,
14781478
)
1479+
if annotation_class:
1480+
return annotation_class.dict()
1481+
raise AppException("Failed to create annotation class")
14791482

14801483
def delete_annotation_class(
1481-
self, project: NotEmptyStr, annotation_class: Union[dict, NotEmptyStr]
1484+
self,
1485+
project: Union[NotEmptyStr, dict, Project],
1486+
annotation_class: Union[dict, NotEmptyStr],
14821487
):
14831488
"""Deletes annotation class from project
14841489
@@ -1489,24 +1494,32 @@ def delete_annotation_class(
14891494
:type annotation_class: str or dict
14901495
"""
14911496

1492-
if isinstance(annotation_class, str):
1493-
try:
1494-
annotation_class = AnnotationClassEntity(
1495-
name=annotation_class,
1496-
color="#ffffff", # noqa Random, just need to serialize
1497-
)
1498-
except ValidationError as e:
1499-
raise AppException(wrap_error(e))
1497+
if isinstance(annotation_class, dict) and "name" in annotation_class.keys():
1498+
class_name = annotation_class["name"]
1499+
elif isinstance(annotation_class, str):
1500+
class_name = annotation_class
15001501
else:
1501-
annotation_class = AnnotationClassEntity(**annotation_class)
1502-
project = self.controller.projects.get_by_name(project).data
1502+
raise AppException("Invalid value provided for annotation_class.")
15031503

1504-
self.controller.annotation_classes.delete(
1505-
project=project, annotation_class=annotation_class
1506-
)
1504+
if isinstance(project, Project):
1505+
project = project.dict()
1506+
1507+
project_name, _ = extract_project_folder(project)
1508+
project = self.controller.get_project(project_name)
1509+
1510+
condition = Condition("name", class_name, EQ) & Condition("pattern", True, EQ)
1511+
annotation_classes = project.list_annotation_classes(condition=condition)
1512+
if annotation_classes:
1513+
class_to_delete = annotation_classes[0]
1514+
logger.info(
1515+
"Deleting annotation class from project %s with name %s",
1516+
project.name,
1517+
class_to_delete.name,
1518+
)
1519+
project.delete_annotation_class(class_id=class_to_delete.id)
15071520

15081521
def download_annotation_classes_json(
1509-
self, project: NotEmptyStr, folder: Union[str, Path]
1522+
self, project: Union[NotEmptyStr, dict, Project], folder: Union[str, Path]
15101523
):
15111524
"""Downloads project classes.json to folder
15121525
@@ -1519,20 +1532,28 @@ def download_annotation_classes_json(
15191532
:return: path of the download file
15201533
:rtype: str
15211534
"""
1535+
if isinstance(project, Project):
1536+
project = project.dict()
15221537

1523-
project = self.controller.projects.get_by_name(project).data
1524-
response = self.controller.annotation_classes.download(
1525-
project=project, download_path=folder
1538+
project_name, _ = extract_project_folder(project)
1539+
project = self.controller.get_project(project_name)
1540+
logger.info(
1541+
f"Downloading classes.json from project {project.name} to folder {str(folder)}."
15261542
)
1527-
if response.errors:
1528-
raise AppException(response.errors)
1529-
return response.data
1543+
annotation_classes: List[dict] = [
1544+
annotation_class.dict()
1545+
for annotation_class in project.list_annotation_classes()
1546+
]
1547+
json_path = f"{folder}/classes.json"
1548+
with open(json_path, "w") as f:
1549+
json.dump(annotation_classes, f, indent=4)
1550+
return json_path
15301551

15311552
def create_annotation_classes_from_classes_json(
15321553
self,
1533-
project: Union[NotEmptyStr, dict],
1554+
project: Union[NotEmptyStr, dict, Project],
15341555
classes_json: Union[List[AnnotationClassEntity], str, Path],
1535-
from_s3_bucket=False,
1556+
from_s3_bucket: str = None,
15361557
):
15371558
"""Creates annotation classes in project from a SuperAnnotate format
15381559
annotation classes.json.
@@ -1549,7 +1570,7 @@ def create_annotation_classes_from_classes_json(
15491570
:return: list of created annotation class metadatas
15501571
:rtype: list of dicts
15511572
"""
1552-
if isinstance(classes_json, str) or isinstance(classes_json, Path):
1573+
if isinstance(classes_json, (str, Path)):
15531574
if from_s3_bucket:
15541575
from_session = boto3.Session()
15551576
from_s3 = from_session.resource("s3")
@@ -1561,12 +1582,12 @@ def create_annotation_classes_from_classes_json(
15611582
else:
15621583
data = open(classes_json, encoding="utf-8")
15631584
classes_json = json.load(data)
1564-
# try:
1565-
# # annotation_classes = parse_obj_as(List[AnnotationClassEntity], classes_json)
1566-
# annotation_classes = [AnnotationClassEntity.from_json(i) for i in classes_json]
1567-
# except ValidationError as _:
1568-
# raise AppException("Couldn't validate annotation classes.")
1569-
project = self.controller.get_project(project)
1585+
1586+
if isinstance(project, Project):
1587+
project = project.dict()
1588+
1589+
project_name, _ = extract_project_folder(project)
1590+
project = self.controller.get_project(project_name)
15701591
annotation_classes = project.create_annotation_classes(classes_json)
15711592
return [i.dict() for i in annotation_classes]
15721593

@@ -1684,7 +1705,10 @@ def download_image(
16841705
return response.data
16851706

16861707
def upload_annotations(
1687-
self, project: NotEmptyStr, annotations: List[dict], keep_status: bool = False
1708+
self,
1709+
project: Union[NotEmptyStr, dict],
1710+
annotations: List[dict],
1711+
keep_status: bool = False,
16881712
):
16891713
"""Uploads a list of annotation dicts as annotations to the SuperAnnotate directory.
16901714

src/superannotate/lib/core/usecases/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from lib.core.usecases.annotations import * # noqa: F403 F401
2-
from lib.core.usecases.classes import * # noqa: F403 F401
32
from lib.core.usecases.images import * # noqa: F403 F401
43
from lib.core.usecases.integrations import * # noqa: F403 F401
54
from lib.core.usecases.items import * # noqa: F403 F401

0 commit comments

Comments
 (0)