Skip to content

Commit 74e8d46

Browse files
authored
Merge pull request #474 from superannotateai/1191_default_attributes
Added default_value handeling
2 parents 3a3e658 + fd6c268 commit 74e8d46

File tree

6 files changed

+167
-10
lines changed

6 files changed

+167
-10
lines changed

src/superannotate/lib/core/reporter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def log_warning(self, value: str):
9696
self.warning_messages.append(value)
9797

9898
def log_error(self, value: str):
99-
self.logger.warning(value)
99+
self.logger.error(value)
100100

101101
def log_debug(self, value: str):
102102
if self._log_debug:

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def __init__(
120120
def execute(self) -> Response:
121121
uploaded_items, failed_items = [], []
122122
self.reporter.log_info(
123-
" Validating metadata against the schema of the custom fields. "
123+
"Validating metadata against the schema of the custom fields. "
124124
"Valid metadata will be attached to the specified item."
125125
)
126126
with self.reporter.spinner:
@@ -138,11 +138,13 @@ def execute(self) -> Response:
138138

139139
if failed_items:
140140
self.reporter.log_error(
141-
f"""The metadata dicts of {len(failed_items)} items are invalid because they don't match
142-
the schema of the custom fields defined for the "{self._project.name}" project."""
141+
f"The metadata dicts of {len(failed_items)} items are invalid because they don't match"
142+
f'the schema of the custom fields defined for the "{self._project.name}" project.'
143143
)
144144
self._response.data = {
145-
"succeeded": list({list(item)[0] for item in self._items} ^ set(failed_items)),
145+
"succeeded": list(
146+
{list(item)[0] for item in self._items} ^ set(failed_items)
147+
),
146148
"failed": failed_items,
147149
}
148150
return self._response

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1953,7 +1953,7 @@ def validate_uniqueness(self):
19531953
if annotation_class.name == self._annotation_class.name
19541954
]
19551955
):
1956-
raise AppValidationException("Annotation class already exits.")
1956+
raise AppValidationException("Annotation class already exists.")
19571957

19581958
def validate_project_type(self):
19591959
if (
@@ -1964,6 +1964,15 @@ def validate_project_type(self):
19641964
f"Predefined tagging functionality is not supported for projects of type {ProjectType.get_name(self._project.type)}."
19651965
)
19661966

1967+
def validate_default_value(self):
1968+
if self._project.type == ProjectType.PIXEL.value and any(
1969+
getattr(attr_group, "default_value", None)
1970+
for attr_group in getattr(self._annotation_class, "attribute_groups", [])
1971+
):
1972+
raise AppException(
1973+
'The "default_value" key is not supported for project type Pixel.'
1974+
)
1975+
19671976
def execute(self):
19681977
if self.is_valid():
19691978
logger.info(
@@ -2079,6 +2088,17 @@ def validate_project_type(self):
20792088
f"Predefined tagging functionality is not supported for projects of type {ProjectType.get_name(self._project.type)}."
20802089
)
20812090

2091+
def validate_default_value(self):
2092+
if self._project.type == ProjectType.PIXEL.value:
2093+
for annotation_class in self._annotation_classes:
2094+
if any(
2095+
getattr(attr_group, "default_value", None)
2096+
for attr_group in getattr(annotation_class, "attribute_groups", [])
2097+
):
2098+
raise AppException(
2099+
'The "default_value" key is not supported for project type Pixel.'
2100+
)
2101+
20822102
def execute(self):
20832103
if self.is_valid():
20842104
existing_annotation_classes = self._annotation_classes_repo.get_all()

tests/integration/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest import TestCase
22

33
from src.superannotate import SAClient
4-
from tests import DATA_SET_PATH
4+
55

66
sa = SAClient()
77

tests/integration/classes/test_create_annotation_class.py

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import os
2-
from os.path import dirname
32
import tempfile
43

4+
from src.superannotate import AppException
55
from src.superannotate import SAClient
6-
sa = SAClient()
7-
from tests.integration.base import BaseTestCase
86
from tests import DATA_SET_PATH
7+
from tests.integration.base import BaseTestCase
8+
9+
sa = SAClient()
910

1011

1112
class TestCreateAnnotationClass(BaseTestCase):
@@ -33,6 +34,46 @@ def test_hex_color_adding(self):
3334
classes = sa.search_annotation_classes(self.PROJECT_NAME, "test_add")
3435
assert classes[0]["color"] == "#0000FF"
3536

37+
def test_create_annotation_class_with_default_attribute(self):
38+
sa.create_annotation_class(
39+
self.PROJECT_NAME,
40+
"test_add",
41+
"#FF0000",
42+
class_type="tag",
43+
attribute_groups=[
44+
{
45+
"name": "test",
46+
"attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}],
47+
"default_value": "Bus"
48+
}
49+
]
50+
)
51+
classes = sa.search_annotation_classes(self.PROJECT_NAME)
52+
assert classes[0]['attribute_groups'][0]["default_value"] == "Bus"
53+
54+
def test_create_annotation_classes_with_default_attribute(self):
55+
sa.create_annotation_classes_from_classes_json(
56+
self.PROJECT_NAME,
57+
classes_json=[
58+
{
59+
"name": "Personal vehicle",
60+
"color": "#ecb65f",
61+
"count": 25,
62+
"createdAt": "2020-10-12T11:35:20.000Z",
63+
"updatedAt": "2020-10-12T11:48:19.000Z",
64+
"attribute_groups": [
65+
{
66+
"name": "test",
67+
"attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}],
68+
"default_value": "Bus"
69+
}
70+
]
71+
}
72+
]
73+
)
74+
classes = sa.search_annotation_classes(self.PROJECT_NAME)
75+
assert classes[0]['attribute_groups'][0]["default_value"] == "Bus"
76+
3677

3778
class TestCreateAnnotationClassNonVectorWithError(BaseTestCase):
3879
PROJECT_NAME = "TestCreateAnnotationClassNonVectorWithError"
@@ -95,3 +136,50 @@ def test_create_annotation_class(self):
95136
msg = str(e)
96137
self.assertEqual(msg, "Predefined tagging functionality is not supported for projects of type Video.")
97138

139+
140+
class TestCreateAnnotationClassPixel(BaseTestCase):
141+
PROJECT_NAME = "TestCreateAnnotationClassPixel"
142+
PROJECT_TYPE = "Pixel"
143+
PROJECT_DESCRIPTION = "Example "
144+
TEST_LARGE_CLASSES_JSON = "large_classes_json.json"
145+
146+
@property
147+
def large_json_path(self):
148+
return os.path.join(DATA_SET_PATH, self.TEST_LARGE_CLASSES_JSON)
149+
150+
def test_create_annotation_class_with_default_attribute(self):
151+
with self.assertRaisesRegexp(AppException, 'The "default_value" key is not supported for project type Pixel.'):
152+
sa.create_annotation_class(
153+
self.PROJECT_NAME,
154+
"test_add",
155+
"#FF0000",
156+
attribute_groups=[
157+
{
158+
"name": "test",
159+
"attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}],
160+
"default_value": "Bus"
161+
}
162+
]
163+
)
164+
165+
def test_create_annotation_classes_with_default_attribute(self):
166+
with self.assertRaisesRegexp(AppException, 'The "default_value" key is not supported for project type Pixel.'):
167+
sa.create_annotation_classes_from_classes_json(
168+
self.PROJECT_NAME,
169+
classes_json=[
170+
{
171+
"name": "Personal vehicle",
172+
"color": "#ecb65f",
173+
"count": 25,
174+
"createdAt": "2020-10-12T11:35:20.000Z",
175+
"updatedAt": "2020-10-12T11:48:19.000Z",
176+
"attribute_groups": [
177+
{
178+
"name": "test",
179+
"attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}],
180+
"default_value": "Bus"
181+
}
182+
]
183+
}
184+
]
185+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from unittest import TestCase
2+
3+
from src.superannotate import SAClient
4+
5+
sa = SAClient()
6+
7+
8+
class CreateProjectFromMetadata(TestCase):
9+
PROJECT_1 = "pr_1"
10+
PROJECT_2 = "pr_2"
11+
12+
def setUp(self) -> None:
13+
self.tearDown()
14+
15+
def tearDown(self) -> None:
16+
for project_name in self.PROJECT_1, self.PROJECT_2:
17+
try:
18+
sa.delete_project(project_name)
19+
except Exception:
20+
pass
21+
22+
def test_create_project_with_default_attribute(self):
23+
sa.create_project(self.PROJECT_1, project_type="Vector", project_description="Desc")
24+
sa.create_annotation_classes_from_classes_json(
25+
self.PROJECT_1,
26+
classes_json=[
27+
{
28+
"name": "Personal vehicle",
29+
"color": "#ecb65f",
30+
"count": 25,
31+
"createdAt": "2020-10-12T11:35:20.000Z",
32+
"updatedAt": "2020-10-12T11:48:19.000Z",
33+
"attribute_groups": [
34+
{
35+
"name": "test",
36+
"attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}],
37+
"default_value": "Bus"
38+
}
39+
]
40+
}
41+
]
42+
)
43+
pr_1_metadata = sa.get_project_metadata(self.PROJECT_1, include_annotation_classes=True)
44+
pr_1_metadata["name"] = self.PROJECT_2
45+
sa.create_project_from_metadata(pr_1_metadata)
46+
pr_2_metadata = sa.get_project_metadata(self.PROJECT_2, include_annotation_classes=True)
47+
assert pr_2_metadata["classes"][0]["attribute_groups"][0]["default_value"] == "Bus"

0 commit comments

Comments
 (0)