Skip to content

Commit 4474304

Browse files
authored
Merge pull request #305 from superannotateai/489
489
2 parents 03c8753 + 525720e commit 4474304

File tree

13 files changed

+221
-89
lines changed

13 files changed

+221
-89
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3641,6 +3641,7 @@ def attach_document_urls_to_project(
36413641
raise AppException(use_case.response.errors)
36423642

36433643

3644+
@Trackable
36443645
@validate_arguments
36453646
def validate_annotations(
36463647
project_type: ProjectTypes, annotations_json: Union[NotEmptyStr, Path]
@@ -3657,7 +3658,9 @@ def validate_annotations(
36573658
"""
36583659
with open(annotations_json) as file:
36593660
annotation_data = json.loads(file.read())
3660-
response = controller.validate_annotations(project_type, annotation_data, allow_extra=False)
3661+
response = controller.validate_annotations(
3662+
project_type, annotation_data, allow_extra=False
3663+
)
36613664
if response.errors:
36623665
raise AppException(response.errors)
36633666
is_valid, _ = response.data

src/superannotate/lib/app/mixp/utils/parsers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,3 +1522,13 @@ def attach_document_urls_to_project(*args, **kwargs):
15221522

15231523
def delete_annotations(*args, **kwargs):
15241524
return {"event_name": "delete_annotations", "properties": {}}
1525+
1526+
1527+
def validate_annotations(*args, **kwargs):
1528+
project_type = kwargs.get("project_type", None)
1529+
if not project_type:
1530+
project_type = args[0]
1531+
return {
1532+
"event_name": "validate_annotations",
1533+
"properties": {"Project Type": project_type},
1534+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
from lib.core.entities.utils import BaseImageInstance
55
from lib.core.entities.utils import BaseModel
6+
from lib.core.entities.utils import Comment
67
from lib.core.entities.utils import Metadata
78
from lib.core.entities.utils import Tag
8-
from lib.core.entities.utils import Comment
99
from pydantic import Field
1010
from pydantic import validator
1111
from pydantic.color import Color

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

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
from pydantic import conlist
88
from pydantic import constr
99
from pydantic import EmailStr
10-
from pydantic import Field
1110
from pydantic import Extra
11+
from pydantic import Field
12+
from pydantic import StrRegexError
1213
from pydantic import validator
1314
from pydantic.errors import EnumMemberError
1415

@@ -26,9 +27,12 @@ def enum_error_handling(self) -> str:
2627

2728
DATE_REGEX = r"\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d{3})Z"
2829

30+
DATE_TIME_FORMAT_ERROR_MESSAGE = (
31+
"does not match expected format YYYY-MM-DDTHH:MM:SS.fffZ"
32+
)
2933

30-
class BaseModel(PyDanticBaseModel):
3134

35+
class BaseModel(PyDanticBaseModel):
3236
class Config:
3337
extra = Extra.allow
3438
use_enum_values = True
@@ -86,7 +90,7 @@ class Attribute(BaseModel):
8690

8791

8892
class Tag(BaseModel):
89-
__root__: str
93+
__root__: NotEmptyStr
9094

9195

9296
class AttributeGroup(BaseModel):
@@ -106,6 +110,22 @@ class TimedBaseModel(BaseModel):
106110
created_at: constr(regex=DATE_REGEX) = Field(None, alias="createdAt")
107111
updated_at: constr(regex=DATE_REGEX) = Field(None, alias="updatedAt")
108112

113+
@validator("created_at", pre=True)
114+
def validate_created_at(cls, value):
115+
try:
116+
constr(regex=DATE_REGEX).validate(value)
117+
except (TypeError, StrRegexError):
118+
raise TypeError(DATE_TIME_FORMAT_ERROR_MESSAGE)
119+
return value
120+
121+
@validator("updated_at", pre=True)
122+
def validate_updated_at(cls, value):
123+
try:
124+
constr(regex=DATE_REGEX).validate(value)
125+
except (TypeError, StrRegexError):
126+
raise TypeError(DATE_TIME_FORMAT_ERROR_MESSAGE)
127+
return value
128+
109129

110130
class UserAction(BaseModel):
111131
email: EmailStr
@@ -130,8 +150,8 @@ class LastUserAction(BaseModel):
130150

131151

132152
class BaseInstance(TrackableModel, TimedBaseModel):
133-
class_id: Optional[str] = Field(None, alias="classId")
134-
class_name: Optional[str] = Field(None, alias="className")
153+
class_id: Optional[int] = Field(None, alias="classId")
154+
class_name: Optional[NotEmptyStr] = Field(None, alias="className")
135155

136156

137157
class MetadataBase(BaseModel):
@@ -147,7 +167,7 @@ class MetadataBase(BaseModel):
147167

148168

149169
class PointLabels(BaseModel):
150-
__root__: Dict[constr(regex=r"^[0-9]*$"), str] # noqa: F722 E261
170+
__root__: Dict[constr(regex=r"^[0-9]*$"), NotEmptyStr] # noqa: F722 E261
151171

152172

153173
class Correspondence(BaseModel):
@@ -164,7 +184,7 @@ class Comment(TimedBaseModel, TrackableModel):
164184

165185
class BaseImageInstance(BaseInstance):
166186
class_id: Optional[int] = Field(None, alias="classId")
167-
class_name: str = Field(alias="className")
187+
class_name: NotEmptyStr = Field(alias="className")
168188
visible: Optional[bool]
169189
locked: Optional[bool]
170190
probability: Optional[int] = Field(100)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from lib.core.entities.utils import BboxPoints
88
from lib.core.entities.utils import Comment
99
from lib.core.entities.utils import Metadata
10+
from lib.core.entities.utils import NotEmptyStr
1011
from lib.core.entities.utils import Tag
1112
from lib.core.entities.utils import VectorAnnotationTypeEnum
1213
from pydantic import conlist
@@ -73,7 +74,7 @@ class Template(BaseVectorInstance):
7374
points: conlist(TemplatePoint, min_items=1)
7475
connections: List[TemplateConnection]
7576
template_id: Optional[int] = Field(None, alias="templateId")
76-
template_name: str = Field(alias="templateName")
77+
template_name: NotEmptyStr = Field(alias="templateName")
7778

7879

7980
class CuboidPoint(BaseModel):
@@ -113,7 +114,7 @@ def return_action(cls, values):
113114
try:
114115
instance_type = values["type"]
115116
except KeyError:
116-
raise ValueError("meta.type required")
117+
raise ValueError("metadata.type required")
117118
try:
118119
return ANNOTATION_TYPES[instance_type](**values)
119120
except KeyError:

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from enum import Enum
2-
from typing import Dict
32
from typing import List
43
from typing import Optional
54
from typing import Union
@@ -9,6 +8,7 @@
98
from lib.core.entities.utils import BaseModel
109
from lib.core.entities.utils import BboxPoints
1110
from lib.core.entities.utils import MetadataBase
11+
from lib.core.entities.utils import NotEmptyStr
1212
from lib.core.entities.utils import PointLabels
1313
from lib.core.entities.utils import Tag
1414
from pydantic import conlist
@@ -40,7 +40,7 @@ class EventTimeStamp(BaseTimeStamp):
4040

4141
class InstanceMetadata(BaseInstance):
4242
type: VideoType
43-
class_name: Optional[str] = Field(alias="className")
43+
class_name: Optional[NotEmptyStr] = Field(alias="className")
4444
point_labels: Optional[PointLabels] = Field(None, alias="pointLabels")
4545
start: int
4646
end: int
@@ -57,14 +57,6 @@ class EventInstanceMetadata(InstanceMetadata):
5757
type: VideoType = Field(VideoType.EVENT.value, const=True)
5858

5959

60-
class BaseVideoInstance(BaseModel):
61-
metadata: InstanceMetadata
62-
id: Optional[str]
63-
type: VideoType
64-
locked: Optional[bool]
65-
timeline: Dict[float, BaseTimeStamp]
66-
67-
6860
class BaseParameter(BaseModel):
6961
start: int
7062
end: int

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3154,11 +3154,11 @@ def execute(self):
31543154

31553155
class ValidateAnnotationUseCase(BaseUseCase):
31563156
def __init__(
3157-
self,
3158-
project_type: str,
3159-
annotation: dict,
3160-
validators: BaseAnnotationValidator,
3161-
allow_extra: bool = True
3157+
self,
3158+
project_type: str,
3159+
annotation: dict,
3160+
validators: BaseAnnotationValidator,
3161+
allow_extra: bool = True,
31623162
):
31633163
super().__init__()
31643164
self._project_type = project_type

src/superannotate/lib/infrastructure/controller.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,10 +1642,13 @@ def delete_annotations(
16421642
)
16431643
return use_case.execute()
16441644

1645-
def validate_annotations(self, project_type: str, annotation: dict, allow_extra: bool = False):
1645+
def validate_annotations(
1646+
self, project_type: str, annotation: dict, allow_extra: bool = False
1647+
):
16461648
use_case = usecases.ValidateAnnotationUseCase(
1647-
project_type, annotation,
1649+
project_type,
1650+
annotation,
16481651
validators=self.annotation_validators,
1649-
allow_extra=allow_extra
1652+
allow_extra=allow_extra,
16501653
)
16511654
return use_case.execute()

src/superannotate/lib/infrastructure/validators.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ def wrap_error(e: ValidationError) -> str:
2222
error_messages = defaultdict(list)
2323
for error in e.errors():
2424
errors_list = list(error["loc"])
25-
errors_list[1::2] = [f"[{i}]" for i in errors_list[1::2]]
26-
errors_list[2::2] = [f".{i}" for i in errors_list[2::2]]
25+
errors_list[1::] = [
26+
f"[{i}]" if isinstance(i, int) else f".{i}" for i in errors_list[1::]
27+
]
2728
error_messages["".join(errors_list)].append(error["msg"])
2829
texts = ["\n"]
2930
for field, text in error_messages.items():
Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,38 @@
1-
{"metadata":{"name":"text_file_example_1","status":"Completed","url":"https://sa-public-files.s3.us-west-2.amazonaws.com/Text+project/text_file_example_1.txt","projectId":160158,"annotatorEmail":null,"qaEmail":null,"lastAction":{"email":"shab.prog@gmail.com","timestamp":1634899229953}},"instances":[{"start":253,"end":593,"classId":873208,"createdAt":"2021-10-22T10:40:26.151Z","createdBy":{"email":"shab.prog@gmail.com","role":"Admin"},"updatedAt":"2021-10-22T10:40:29.953Z","updatedBy":{"email":"shab.prog@gmail.com","role":"Admin"},"attributes":[],"creationType":"Manual","className":"vid"}],"tags":["vid"],"freeText":""}
1+
{
2+
"metadata": {
3+
"name": "text_file_example_1",
4+
"status": "Completed",
5+
"url": "https://sa-public-files.s3.us-west-2.amazonaws.com/Text+project/text_file_example_1.txt",
6+
"projectId": 160158,
7+
"annotatorEmail": null,
8+
"qaEmail": null,
9+
"lastAction": {
10+
"email": "shab.prog@gmail.com",
11+
"timestamp": 1634899229953
12+
}
13+
},
14+
"instances": [
15+
{
16+
"start": 253,
17+
"end": 593,
18+
"classId": 873208,
19+
"createdAt": "2021-10-22T10:40:26.151Z",
20+
"createdBy": {
21+
"email": "shab.prog@gmail.com",
22+
"role": "Admin"
23+
},
24+
"updatedAt": "2021-10-22T10:40:29.953Z",
25+
"updatedBy": {
26+
"email": "shab.prog@gmail.com",
27+
"role": "Admin"
28+
},
29+
"attributes": [],
30+
"creationType": "Manual",
31+
"className": "vid"
32+
}
33+
],
34+
"tags": [
35+
"vid"
36+
],
37+
"freeText": ""
38+
}

0 commit comments

Comments
 (0)