Skip to content

Commit a38af36

Browse files
authored
Merge pull request #479 from superannotateai/group_type
added group_type handeling
2 parents a72cd7e + 866b656 commit a38af36

File tree

17 files changed

+481
-224
lines changed

17 files changed

+481
-224
lines changed

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@
4343
from lib.core import LIMITED_FUNCTIONS
4444
from lib.core.entities import AttachmentEntity
4545
from lib.core.entities import SettingEntity
46+
from lib.core.entities.classes import AnnotationClassEntity
4647
from lib.core.entities.integrations import IntegrationEntity
47-
from lib.core.entities.project_entities import AnnotationClassEntity
4848
from lib.core.enums import ImageQuality
4949
from lib.core.exceptions import AppException
5050
from lib.core.types import AttributeGroup
5151
from lib.core.types import MLModel
5252
from lib.core.types import PriorityScore
5353
from lib.core.types import Project
5454
from lib.infrastructure.controller import Controller
55+
from lib.infrastructure.validators import wrap_error
5556
from pydantic import conlist
5657
from pydantic import parse_obj_as
5758
from pydantic import StrictBool
@@ -580,8 +581,7 @@ def search_annotation_classes(
580581
"""
581582
project_name, folder_name = extract_project_folder(project)
582583
classes = self.controller.search_annotation_classes(project_name, name_contains)
583-
classes = [BaseSerializer(attribute).serialize() for attribute in classes.data]
584-
return classes
584+
return BaseSerializer.serialize_iterable(classes.data)
585585

586586
def set_project_default_image_quality_in_editor(
587587
self,
@@ -1160,12 +1160,18 @@ def create_annotation_class(
11601160
attribute_groups = (
11611161
list(map(lambda x: x.dict(), attribute_groups)) if attribute_groups else []
11621162
)
1163+
try:
1164+
annotation_class = AnnotationClassEntity(
1165+
name=name,
1166+
color=color, # noqa
1167+
attribute_groups=attribute_groups,
1168+
type=class_type, # noqa
1169+
)
1170+
except ValidationError as e:
1171+
raise AppException(wrap_error(e))
1172+
11631173
response = self.controller.create_annotation_class(
1164-
project_name=project,
1165-
name=name,
1166-
color=color,
1167-
attribute_groups=attribute_groups,
1168-
class_type=class_type,
1174+
project_name=project, annotation_class=annotation_class
11691175
)
11701176
if response.errors:
11711177
raise AppException(response.errors)
@@ -1238,9 +1244,8 @@ def create_annotation_classes_from_classes_json(
12381244
classes_json = json.load(data)
12391245
try:
12401246
annotation_classes = parse_obj_as(List[AnnotationClassEntity], classes_json)
1241-
except ValidationError:
1247+
except ValidationError as _:
12421248
raise AppException("Couldn't validate annotation classes.")
1243-
logger.info(f"Creating annotation classes in project {project}.")
12441249
response = self.controller.create_annotation_classes(
12451250
project_name=project,
12461251
annotation_classes=annotation_classes,

src/superannotate/lib/app/serializers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ def _serialize(
4949
flat: bool = False,
5050
exclude: Set[str] = None,
5151
):
52+
if not entity:
53+
return None
5254
if isinstance(entity, dict):
5355
return entity
5456
if isinstance(entity, BaseModel):

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from lib.core.entities.base import ProjectEntity
44
from lib.core.entities.base import SettingEntity
55
from lib.core.entities.base import SubSetEntity
6+
from lib.core.entities.classes import AnnotationClassEntity
67
from lib.core.entities.integrations import IntegrationEntity
78
from lib.core.entities.items import DocumentEntity
89
from lib.core.entities.items import TmpImageEntity
910
from lib.core.entities.items import VideoEntity
10-
from lib.core.entities.project_entities import AnnotationClassEntity
1111
from lib.core.entities.project_entities import BaseEntity
1212
from lib.core.entities.project_entities import ConfigEntity
1313
from lib.core.entities.project_entities import FolderEntity
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from enum import Enum
2+
from typing import List
3+
from typing import Optional
4+
5+
from lib.core.enums import BaseTitledEnum
6+
from lib.core.enums import ClassTypeEnum
7+
from pydantic import BaseModel as BasePydanticModel
8+
from pydantic import Extra
9+
from pydantic import StrictInt
10+
from pydantic import StrictStr
11+
from pydantic import validator
12+
from pydantic.color import Color
13+
from pydantic.color import ColorType
14+
15+
16+
DATE_REGEX = r"\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d{3})Z"
17+
DATE_TIME_FORMAT_ERROR_MESSAGE = (
18+
"does not match expected format YYYY-MM-DDTHH:MM:SS.fffZ"
19+
)
20+
21+
22+
class HexColor(BasePydanticModel):
23+
__root__: ColorType
24+
25+
@validator("__root__")
26+
def validate_color(cls, v):
27+
return "#{:02X}{:02X}{:02X}".format(*Color(v).as_rgb_tuple())
28+
29+
30+
class BaseModel(BasePydanticModel):
31+
class Config:
32+
extra = Extra.allow
33+
error_msg_templates = {
34+
"type_error.integer": "integer type expected",
35+
"type_error.string": "str type expected",
36+
"value_error.missing": "field required",
37+
}
38+
39+
def dict(self, *args, fill_enum_values=False, **kwargs):
40+
data = super().dict(*args, **kwargs)
41+
if fill_enum_values:
42+
data = self._fill_enum_values(data)
43+
return data
44+
45+
@staticmethod
46+
def _fill_enum_values(data: dict) -> dict:
47+
for key, val in data.items():
48+
if isinstance(val, BaseTitledEnum):
49+
data[key] = val.__doc__
50+
return data
51+
52+
53+
class GroupTypeEnum(str, Enum):
54+
RADIO = "radio"
55+
CHECKLIST = "checklist"
56+
NUMERIC = "numeric"
57+
TEXT = "text"
58+
59+
60+
class Attribute(BaseModel):
61+
id: Optional[StrictInt]
62+
group_id: Optional[StrictInt]
63+
project_id: Optional[StrictInt]
64+
name: StrictStr
65+
66+
def __hash__(self):
67+
return hash(f"{self.id}{self.group_id}{self.name}")
68+
69+
70+
class AttributeGroup(BaseModel):
71+
id: Optional[StrictInt]
72+
group_type: Optional[GroupTypeEnum]
73+
class_id: Optional[StrictInt]
74+
name: StrictStr
75+
is_multiselect: Optional[bool]
76+
attributes: Optional[List[Attribute]]
77+
78+
def __hash__(self):
79+
return hash(f"{self.id}{self.class_id}{self.name}")
80+
81+
82+
class AnnotationClassEntity(BaseModel):
83+
id: Optional[StrictInt]
84+
project_id: Optional[StrictInt]
85+
type: ClassTypeEnum = ClassTypeEnum.OBJECT
86+
name: StrictStr
87+
color: HexColor
88+
attribute_groups: List[AttributeGroup] = []
89+
90+
def __hash__(self):
91+
return hash(f"{self.id}{self.type}{self.name}")
92+
93+
class Config:
94+
validate_assignment = True
95+
exclude_none = True

src/superannotate/lib/core/service_types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class ServiceResponse(BaseModel):
9393
content: Union[bytes, str]
9494
data: Any
9595
count: Optional[int] = 0
96-
_error: str
96+
_error: str = None
9797

9898
class Config:
9999
extra = Extra.allow

src/superannotate/lib/core/serviceproviders.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,10 @@ def get_annotations(
311311
) -> List[dict]:
312312
raise NotImplementedError
313313

314+
@abstractmethod
315+
def create_annotation_classes(self, project_id: int, team_id: int, data: List):
316+
raise NotImplementedError
317+
314318
@abstractmethod
315319
async def download_annotations(
316320
self,
@@ -400,3 +404,9 @@ def delete_custom_fields(
400404
items: List[Dict[str, List[str]]],
401405
) -> ServiceResponse:
402406
raise NotImplementedError
407+
408+
@abstractmethod
409+
def list_annotation_classes(
410+
self, project_id: int, team_id: int, query_string: str = None
411+
):
412+
raise NotImplementedError

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from lib.core.usecases.annotations import * # noqa: F403 F401
2+
from lib.core.usecases.classes import * # noqa: F403 F401
23
from lib.core.usecases.custom_fields import * # noqa: F403 F401
34
from lib.core.usecases.folders import * # noqa: F403 F401
45
from lib.core.usecases.images import * # noqa: F403 F401

0 commit comments

Comments
 (0)