Skip to content

Commit 873cd88

Browse files
authored
Merge pull request #637 from superannotateai/relationship_class_2089
add Relationship class type support
2 parents 89d4aee + 6ad1188 commit 873cd88

File tree

6 files changed

+103
-6
lines changed

6 files changed

+103
-6
lines changed

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
from lib.core.entities.classes import AttributeGroup
5656
from lib.core.entities.integrations import IntegrationEntity
5757
from lib.core.enums import ImageQuality
58+
from lib.core.enums import ProjectType
59+
from lib.core.enums import ClassTypeEnum
5860
from lib.core.exceptions import AppException
5961
from lib.core.types import MLModel
6062
from lib.core.types import PriorityScoreEntity
@@ -72,11 +74,16 @@
7274
PROJECT_STATUS = Literal["NotStarted", "InProgress", "Completed", "OnHold"]
7375

7476
PROJECT_TYPE = Literal[
75-
"Vector", "Pixel", "Video", "Document", "Tiled", "Other", "PointCloud", "CustomEditor"
77+
"Vector",
78+
"Pixel",
79+
"Video",
80+
"Document",
81+
"Tiled",
82+
"Other",
83+
"PointCloud",
84+
"CustomEditor",
7685
]
7786

78-
CLASS_TYPE = Literal["object", "tag"]
79-
8087
ANNOTATION_STATUS = Literal[
8188
"NotStarted", "InProgress", "QualityCheck", "Returned", "Completed", "Skipped"
8289
]
@@ -1310,7 +1317,7 @@ def create_annotation_class(
13101317
name: NotEmptyStr,
13111318
color: NotEmptyStr,
13121319
attribute_groups: Optional[List[AttributeGroup]] = None,
1313-
class_type: CLASS_TYPE = "object",
1320+
class_type: str = "object",
13141321
):
13151322
"""Create annotation class in project
13161323
@@ -1331,7 +1338,7 @@ def create_annotation_class(
13311338
- "name"
13321339
:type attribute_groups: list of dicts
13331340
1334-
:param class_type: class type. Should be either "object" or "tag"
1341+
:param class_type: class type. Should be either "object" or "tag". Document project type can also have "relationship" type of classes.
13351342
:type class_type: str
13361343
13371344
:return: new class metadata
@@ -1405,6 +1412,13 @@ def create_annotation_class(
14051412
except ValidationError as e:
14061413
raise AppException(wrap_error(e))
14071414
project = self.controller.projects.get_by_name(project).data
1415+
if (
1416+
project.type != ProjectType.DOCUMENT
1417+
and annotation_class.type == ClassTypeEnum.RELATIONSHIP
1418+
):
1419+
raise AppException(
1420+
f"{annotation_class.type.name} class type is not supported in {project.type.name} project."
1421+
)
14081422
response = self.controller.annotation_classes.create(
14091423
project=project, annotation_class=annotation_class
14101424
)

src/superannotate/lib/core/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class AnnotationStatus(BaseTitledEnum):
157157
class ClassTypeEnum(BaseTitledEnum):
158158
OBJECT = "object", 1
159159
TAG = "tag", 2
160+
RELATIONSHIP = "relationship", 3
160161

161162
@classmethod
162163
def get_value(cls, name):

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,10 @@ def get_annotation_from_s3(bucket, path: str):
573573

574574
def prepare_annotation(self, annotation: dict, size) -> dict:
575575
errors = None
576-
if size < BIG_FILE_THRESHOLD and self._project.type < constants.ProjectType.PIXEL.value:
576+
if (
577+
size < BIG_FILE_THRESHOLD
578+
and self._project.type < constants.ProjectType.PIXEL.value
579+
):
577580
use_case = ValidateAnnotationUseCase(
578581
reporter=self.reporter,
579582
team_id=self._project.team_id,

tests/data_set/document_annotation/classes/classes.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,16 @@
2828
]
2929
}
3030
]
31+
},
32+
{
33+
"id": 5175192,
34+
"project_id": 555040,
35+
"name": "apple",
36+
"color": "#bafbcd",
37+
"count": 0,
38+
"type": "relationship",
39+
"createdAt": "2023-08-24T14:45:13.000Z",
40+
"updatedAt": "2023-08-24T14:45:13.000Z",
41+
"attribute_groups": []
3142
}
3243
]

tests/integration/annotations/test_download_annotations.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,50 @@ def test_download_annotations_duplicated_names(self):
131131
assert (
132132
"INFO:sa:Dropping duplicates. Found 1/4 unique items." in cm.output
133133
)
134+
135+
136+
class TestDocumentProjectDownloadAnnotations(BaseTestCase):
137+
PROJECT_NAME = "Test-document-project-download_annotations"
138+
FOLDER_NAME = "FOLDER_NAME"
139+
FOLDER_NAME_2 = "FOLDER_NAME_2"
140+
PROJECT_DESCRIPTION = "Desc"
141+
PROJECT_TYPE = "Document"
142+
TEST_FOLDER_PATH = "data_set/document_annotation"
143+
ITEM_NAME = "text_file_example_1"
144+
145+
@property
146+
def folder_path(self):
147+
return os.path.join(Path(__file__).parent.parent.parent, self.TEST_FOLDER_PATH)
148+
149+
def test_document_project_download_annotations(self):
150+
sa.attach_items(
151+
self.PROJECT_NAME,
152+
[
153+
{
154+
"name": self.ITEM_NAME,
155+
"url": "https://sa-public-files.s3.us-west-2.amazonaws.com/Text+project/text_file_example_1.txt",
156+
}
157+
],
158+
)
159+
sa.create_annotation_classes_from_classes_json(
160+
self.PROJECT_NAME, f"{self.folder_path}/classes/classes.json"
161+
)
162+
_, _, _ = sa.upload_annotations_from_folder_to_project(
163+
self.PROJECT_NAME, self.folder_path
164+
)
165+
with tempfile.TemporaryDirectory() as temp_dir:
166+
annotations_path = sa.download_annotations(
167+
f"{self.PROJECT_NAME}", temp_dir, [self.ITEM_NAME]
168+
)
169+
self.assertEqual(len(os.listdir(temp_dir)), 2)
170+
with open(
171+
f"{self.folder_path}//{self.ITEM_NAME}.json"
172+
) as pre_annotation_file, open(
173+
f"{annotations_path}/{self.ITEM_NAME}.json"
174+
) as post_annotation_file:
175+
pre_annotation_data = json.load(pre_annotation_file)
176+
post_annotation_data = json.load(post_annotation_file)
177+
self.assertEqual(
178+
len(pre_annotation_data["instances"]),
179+
len(post_annotation_data["instances"]),
180+
)

tests/integration/classes/test_create_annotation_class.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,24 @@ def test_create_annotation_class_with_default_attribute(self):
382382
}
383383
],
384384
)
385+
386+
387+
class TestDocumentAnnotationClasses(BaseTestCase):
388+
PROJECT_NAME = "TestDocumentAnnotationClasses"
389+
PROJECT_DESCRIPTION = "desc"
390+
PROJECT_TYPE = "Document"
391+
392+
def test_document_project_create_annotation_class_search(self):
393+
sa.create_annotation_class(
394+
self.PROJECT_NAME, name="tt", color="#FFFFFF", class_type="relationship"
395+
)
396+
classes = sa.search_annotation_classes(self.PROJECT_NAME)
397+
self.assertEqual(len(classes), 1)
398+
self.assertEqual(classes[0]["type"], "relationship")
399+
self.assertEqual(classes[0]["color"], "#FFFFFF")
400+
sa.create_annotation_class(self.PROJECT_NAME, name="tb", color="#FFFFFF")
401+
# test search
402+
classes = sa.search_annotation_classes(self.PROJECT_NAME, "bb")
403+
self.assertEqual(len(classes), 0)
404+
classes = sa.search_annotation_classes(self.PROJECT_NAME, "tt")
405+
self.assertEqual(len(classes), 1)

0 commit comments

Comments
 (0)