Skip to content

Commit 7d251c2

Browse files
authored
Merge pull request #19 from superannotateai/develop
Develop
2 parents a4d3e9b + 93565c7 commit 7d251c2

File tree

8 files changed

+70
-183
lines changed

8 files changed

+70
-183
lines changed

src/superannotate_schemas/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from superannotate_schemas.validators import AnnotationValidators
88

9-
__version__ = '1.0.41'
9+
__version__ = '1.0.42'
1010

1111
__all__ = [
1212
"__version__",

src/superannotate_schemas/schemas/base.py

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import datetime
12
from typing import Dict
23
from typing import List
4+
from math import isnan
35
from typing import Optional
46
from typing import Union
57

68
from pydantic import BaseModel as PyDanticBaseModel
79
from pydantic import conlist
810
from pydantic import constr
911
from pydantic import Extra
10-
from pydantic import StrictInt
1112
from pydantic import StrictFloat
13+
from pydantic import StrictInt
1214
from pydantic import StrictStr
1315
from pydantic import StrictBool
1416
from pydantic import Field
@@ -18,6 +20,9 @@
1820
from pydantic.errors import EnumMemberError
1921
from pydantic import validator
2022
from pydantic.validators import strict_str_validator
23+
from pydantic.validators import strict_float_validator
24+
from pydantic.validators import strict_int_validator
25+
from pydantic.validators import number_multiple_validator
2126
from pydantic.color import Color
2227
from pydantic.color import ColorType
2328

@@ -59,6 +64,7 @@ def validate(cls, value: Union[str]) -> Union[str]:
5964

6065

6166
class BaseModel(PyDanticBaseModel):
67+
6268
class Config:
6369
extra = Extra.allow
6470
use_enum_values = True
@@ -69,6 +75,45 @@ class Config:
6975
}
7076

7177

78+
class StrictPointNumber(BaseModel):
79+
__root__: Union[StrictInt, StrictFloat]
80+
81+
@classmethod
82+
def __get_validators__(cls):
83+
yield cls._validate_types
84+
85+
@classmethod
86+
def _validate_types(cls, value):
87+
is_valid_float, is_valid_int = True, True
88+
try:
89+
cls._validate_float(value)
90+
except TypeError:
91+
is_valid_float = False
92+
if not is_valid_float:
93+
try:
94+
cls._validate_int(value)
95+
except TypeError:
96+
is_valid_int = False
97+
if is_valid_float or is_valid_int:
98+
return value
99+
raise TypeError("is not a valid number. Integer or float types are expected")
100+
101+
@classmethod
102+
def _validate_int(cls, value):
103+
return strict_int_validator(value)
104+
105+
@classmethod
106+
def _validate_float(cls, value):
107+
strict_float_validator(value)
108+
return cls._validate_nan(value)
109+
110+
@staticmethod
111+
def _validate_nan(v):
112+
if isnan(v):
113+
raise TypeError("NaN is not a valid float")
114+
return v
115+
116+
72117
class AxisPoint(BaseModel):
73118
x: StrictNumber
74119
y: StrictNumber
@@ -96,14 +141,17 @@ class TimedBaseModel(BaseModel):
96141
created_at: Optional[constr(regex=DATE_REGEX)] = Field(None, alias="createdAt")
97142
updated_at: Optional[constr(regex=DATE_REGEX)] = Field(None, alias="updatedAt")
98143

99-
@validator("created_at", "updated_at", pre=True)
144+
@validator("created_at", "updated_at", pre=True, always=True)
100145
def validate_created_at(cls, value):
101-
try:
102-
if value is not None:
103-
constr(regex=DATE_REGEX, strict=True).validate(value)
104-
except (TypeError, StrRegexError):
105-
raise TypeError(DATE_TIME_FORMAT_ERROR_MESSAGE)
106-
return value
146+
if value:
147+
try:
148+
if value is not None:
149+
constr(regex=DATE_REGEX, strict=True).validate(value)
150+
except (TypeError, StrRegexError):
151+
raise TypeError(DATE_TIME_FORMAT_ERROR_MESSAGE)
152+
return value
153+
else:
154+
return datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
107155

108156

109157
class UserAction(BaseModel):

src/superannotate_schemas/schemas/external/pixel.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from superannotate_schemas.schemas.base import BaseAttribute
66
from superannotate_schemas.schemas.base import BaseImageMetadata
77
from superannotate_schemas.schemas.base import NotEmptyStr
8-
from superannotate_schemas.schemas.base import StrictStr
98
from superannotate_schemas.schemas.base import HexColor
109
from superannotate_schemas.schemas.base import Tag
1110
from superannotate_schemas.schemas.base import Comment

src/superannotate_schemas/schemas/external/vector.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
from typing import Union
44

55
from pydantic import Field
6-
from pydantic import StrictFloat
76
from pydantic import StrictInt
87
from pydantic import StrictStr
98
from pydantic import ValidationError
109
from pydantic import conlist
1110
from pydantic.error_wrappers import ErrorWrapper
1211

12+
from superannotate_schemas.schemas.base import StrictPointNumber
1313
from superannotate_schemas.schemas.base import AxisPoint
1414
from superannotate_schemas.schemas.base import BaseAttribute
1515
from superannotate_schemas.schemas.base import BaseImageMetadata
@@ -47,12 +47,12 @@ class Point(VectorInstance, AxisPoint):
4747

4848

4949
class PolyLine(VectorInstance):
50-
points: List[Union[StrictFloat, StrictInt]]
50+
points: List[StrictPointNumber]
5151

5252

5353
class Polygon(VectorInstance):
54-
points: conlist(Union[StrictFloat, StrictInt], min_items=3)
55-
exclude: Optional[List[List[Union[StrictFloat, StrictInt]]]] = []
54+
points: conlist(StrictPointNumber, min_items=3)
55+
exclude: Optional[List[List[StrictPointNumber]]] = []
5656

5757

5858
class Bbox(VectorInstance):

src/superannotate_schemas/schemas/internal/vector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from superannotate_schemas.schemas.base import NotEmptyStr
1919

2020
from pydantic import StrictInt
21-
from pydantic import StrictFloat
21+
from superannotate_schemas.schemas.base import StrictFloat
2222
from pydantic import conlist
2323
from pydantic import Field
2424
from pydantic import validate_model

src/superannotate_schemas/schemas/internal/video.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from superannotate_schemas.schemas.base import AnnotationStatusEnum
1414

1515
from superannotate_schemas.schemas.base import BaseModel
16-
from pydantic import StrictFloat
16+
from superannotate_schemas.schemas.base import StrictFloat
1717
from pydantic import constr
1818
from pydantic import Field
1919
from pydantic import StrictBool

tests/test_multi_instance_list.py

Lines changed: 0 additions & 11 deletions
This file was deleted.

tests/test_validators.py

Lines changed: 7 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from superannotate_schemas.schemas.classes import AnnotationClass
77
from superannotate_schemas.validators import AnnotationValidators
8+
from superannotate_schemas.schemas.external.vector import Polygon
89

910

1011
class TestSchemas(TestCase):
@@ -974,162 +975,6 @@ def test_validate_video_invalid_instance_without_type_and_attr_annotation(self,
974975
self.assertEqual(validator.generate_report(),
975976
"instances[2].meta.type field required")
976977

977-
@patch('builtins.print')
978-
def test_validate_vector_template_polygon_polyline_min_annotation(self, mock_print):
979-
json_name = "test.json"
980-
with tempfile.TemporaryDirectory() as tmpdir_name:
981-
with open(f"{tmpdir_name}/{json_name}", "w") as json_file:
982-
json_file.write(
983-
'''
984-
{
985-
"metadata": {
986-
"lastAction": {
987-
"email": "some@some.com",
988-
"timestamp": 1636964198056
989-
},
990-
"width": "1234",
991-
"height": 1540,
992-
"name": "t.png",
993-
"projectId": 164988,
994-
"isPredicted": false,
995-
"status": "Completed",
996-
"pinned": false,
997-
"annotatorEmail": null,
998-
"qaEmail": null
999-
},
1000-
"comments": [],
1001-
"tags": [],
1002-
"instances": [
1003-
{
1004-
"type": "template",
1005-
"classId": 880080,
1006-
"probability": 100,
1007-
"points": [
1008-
],
1009-
"connections": [
1010-
{
1011-
"id": 1,
1012-
"from": 1,
1013-
"to": 2
1014-
}
1015-
],
1016-
"groupId": 0,
1017-
"pointLabels": {},
1018-
"locked": false,
1019-
"visible": true,
1020-
"attributes": [],
1021-
"templateId": 4728,
1022-
"trackingId": null,
1023-
"error": null,
1024-
"createdAt": "2021-11-15T08:24:40.712Z",
1025-
"createdBy": {
1026-
"email": "shab.prog@gmail.com",
1027-
"role": "Admin"
1028-
},
1029-
"creationType": "Manual",
1030-
"updatedAt": "2021-11-15T08:24:46.440Z",
1031-
"updatedBy": {
1032-
"email": "shab.prog@gmail.com",
1033-
"role": "Admin"
1034-
},
1035-
"className": "kj",
1036-
"templateName": "templ1"
1037-
},
1038-
{
1039-
"type": "polygon",
1040-
"classId": 880080,
1041-
"probability": 100,
1042-
"points": [
1043-
233.69
1044-
],
1045-
"groupId": 0,
1046-
"pointLabels": {},
1047-
"locked": true,
1048-
"visible": true,
1049-
"attributes": [],
1050-
"trackingId": null,
1051-
"error": null,
1052-
"createdAt": "2021-11-15T08:18:16.103Z",
1053-
"createdBy": {
1054-
"email": "some@some.com",
1055-
"role": "Admin"
1056-
},
1057-
"creationType": "Manual",
1058-
"updatedAt": "2021-11-15T08:18:20.233Z",
1059-
"updatedBy": {
1060-
"email": "some@some.com",
1061-
"role": "Admin"
1062-
},
1063-
"className": "kj"
1064-
},
1065-
{
1066-
"type": "polyline",
1067-
"classId": 880080,
1068-
"probability": 100,
1069-
"points": [
1070-
218.22
1071-
],
1072-
"groupId": 0,
1073-
"pointLabels": {},
1074-
"locked": false,
1075-
"visible": true,
1076-
"attributes": [],
1077-
"trackingId": null,
1078-
"error": null,
1079-
"createdAt": "2021-11-15T08:18:06.203Z",
1080-
"createdBy": {
1081-
"email": "some@some.com",
1082-
"role": "Admin"
1083-
},
1084-
"creationType": "Manual",
1085-
"updatedAt": "2021-11-15T08:18:13.439Z",
1086-
"updatedBy": {
1087-
"email": "some@some.com",
1088-
"role": "Admin"
1089-
},
1090-
"className": "kj"
1091-
},
1092-
{
1093-
"type": "bbox",
1094-
"classId": 880080,
1095-
"probability": 100,
1096-
"points": {
1097-
"x1": 487.78,
1098-
"x2": 1190.87,
1099-
"y1": 863.91,
1100-
"y2": 1463.78
1101-
},
1102-
"groupId": 0,
1103-
"pointLabels": {},
1104-
"locked": false,
1105-
"visible": true,
1106-
"attributes": [],
1107-
"trackingId": null,
1108-
"error": null,
1109-
"createdAt": "2021-11-15T06:43:09.812Z",
1110-
"createdBy": {
1111-
"email": "some@some.com",
1112-
"role": "Admin"
1113-
},
1114-
"creationType": "Manual",
1115-
"updatedAt": "2021-11-15T08:16:48.807Z",
1116-
"updatedBy": {
1117-
"email": "some@some.com",
1118-
"role": "Admin"
1119-
},
1120-
"className": "kj"
1121-
}
1122-
]
1123-
}
1124-
'''
1125-
)
1126-
1127-
with open(f"{tmpdir_name}/{json_name}", "r") as f:
1128-
data = json.loads(f.read())
1129-
validator = AnnotationValidators.get_validator("vector")(data)
1130-
self.assertFalse(validator.is_valid())
1131-
self.assertEqual(len(validator.generate_report()), 246)
1132-
1133978
def test_validate_video_point_labels(self):
1134979
with tempfile.TemporaryDirectory() as tmpdir_name:
1135980
with open(f"{tmpdir_name}/test_validate_video_point_labels.json",
@@ -2114,3 +1959,9 @@ def test_validate_tag_without_class_name(self):
21141959
self.assertFalse(validator.is_valid())
21151960
print(validator.generate_report())
21161961
self.assertEqual(len(validator.generate_report()), 191)
1962+
1963+
def test_strict_points(self):
1964+
try:
1965+
Polygon(points=["asd", 1, 1.0, 3], type="polygon")
1966+
except Exception as e:
1967+
print(333, e)

0 commit comments

Comments
 (0)