Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/superannotate_schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from superannotate_schemas.validators import AnnotationValidators

__version__ = '1.0.41'
__version__ = '1.0.42'

__all__ = [
"__version__",
Expand Down
64 changes: 56 additions & 8 deletions src/superannotate_schemas/schemas/base.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import datetime
from typing import Dict
from typing import List
from math import isnan
from typing import Optional
from typing import Union

from pydantic import BaseModel as PyDanticBaseModel
from pydantic import conlist
from pydantic import constr
from pydantic import Extra
from pydantic import StrictInt
from pydantic import StrictFloat
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import StrictBool
from pydantic import Field
Expand All @@ -18,6 +20,9 @@
from pydantic.errors import EnumMemberError
from pydantic import validator
from pydantic.validators import strict_str_validator
from pydantic.validators import strict_float_validator
from pydantic.validators import strict_int_validator
from pydantic.validators import number_multiple_validator
from pydantic.color import Color
from pydantic.color import ColorType

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


class BaseModel(PyDanticBaseModel):

class Config:
extra = Extra.allow
use_enum_values = True
Expand All @@ -69,6 +75,45 @@ class Config:
}


class StrictPointNumber(BaseModel):
__root__: Union[StrictInt, StrictFloat]

@classmethod
def __get_validators__(cls):
yield cls._validate_types

@classmethod
def _validate_types(cls, value):
is_valid_float, is_valid_int = True, True
try:
cls._validate_float(value)
except TypeError:
is_valid_float = False
if not is_valid_float:
try:
cls._validate_int(value)
except TypeError:
is_valid_int = False
if is_valid_float or is_valid_int:
return value
raise TypeError("is not a valid number. Integer or float types are expected")

@classmethod
def _validate_int(cls, value):
return strict_int_validator(value)

@classmethod
def _validate_float(cls, value):
strict_float_validator(value)
return cls._validate_nan(value)

@staticmethod
def _validate_nan(v):
if isnan(v):
raise TypeError("NaN is not a valid float")
return v


class AxisPoint(BaseModel):
x: StrictNumber
y: StrictNumber
Expand Down Expand Up @@ -96,14 +141,17 @@ class TimedBaseModel(BaseModel):
created_at: Optional[constr(regex=DATE_REGEX)] = Field(None, alias="createdAt")
updated_at: Optional[constr(regex=DATE_REGEX)] = Field(None, alias="updatedAt")

@validator("created_at", "updated_at", pre=True)
@validator("created_at", "updated_at", pre=True, always=True)
def validate_created_at(cls, value):
try:
if value is not None:
constr(regex=DATE_REGEX, strict=True).validate(value)
except (TypeError, StrRegexError):
raise TypeError(DATE_TIME_FORMAT_ERROR_MESSAGE)
return value
if value:
try:
if value is not None:
constr(regex=DATE_REGEX, strict=True).validate(value)
except (TypeError, StrRegexError):
raise TypeError(DATE_TIME_FORMAT_ERROR_MESSAGE)
return value
else:
return datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"


class UserAction(BaseModel):
Expand Down
1 change: 0 additions & 1 deletion src/superannotate_schemas/schemas/external/pixel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from superannotate_schemas.schemas.base import BaseAttribute
from superannotate_schemas.schemas.base import BaseImageMetadata
from superannotate_schemas.schemas.base import NotEmptyStr
from superannotate_schemas.schemas.base import StrictStr
from superannotate_schemas.schemas.base import HexColor
from superannotate_schemas.schemas.base import Tag
from superannotate_schemas.schemas.base import Comment
Expand Down
8 changes: 4 additions & 4 deletions src/superannotate_schemas/schemas/external/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from typing import Union

from pydantic import Field
from pydantic import StrictFloat
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import ValidationError
from pydantic import conlist
from pydantic.error_wrappers import ErrorWrapper

from superannotate_schemas.schemas.base import StrictPointNumber
from superannotate_schemas.schemas.base import AxisPoint
from superannotate_schemas.schemas.base import BaseAttribute
from superannotate_schemas.schemas.base import BaseImageMetadata
Expand Down Expand Up @@ -47,12 +47,12 @@ class Point(VectorInstance, AxisPoint):


class PolyLine(VectorInstance):
points: List[Union[StrictFloat, StrictInt]]
points: List[StrictPointNumber]


class Polygon(VectorInstance):
points: conlist(Union[StrictFloat, StrictInt], min_items=3)
exclude: Optional[List[List[Union[StrictFloat, StrictInt]]]] = []
points: conlist(StrictPointNumber, min_items=3)
exclude: Optional[List[List[StrictPointNumber]]] = []


class Bbox(VectorInstance):
Expand Down
2 changes: 1 addition & 1 deletion src/superannotate_schemas/schemas/internal/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from superannotate_schemas.schemas.base import NotEmptyStr

from pydantic import StrictInt
from pydantic import StrictFloat
from superannotate_schemas.schemas.base import StrictFloat
from pydantic import conlist
from pydantic import Field
from pydantic import validate_model
Expand Down
2 changes: 1 addition & 1 deletion src/superannotate_schemas/schemas/internal/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from superannotate_schemas.schemas.base import AnnotationStatusEnum

from superannotate_schemas.schemas.base import BaseModel
from pydantic import StrictFloat
from superannotate_schemas.schemas.base import StrictFloat
from pydantic import constr
from pydantic import Field
from pydantic import StrictBool
Expand Down
11 changes: 0 additions & 11 deletions tests/test_multi_instance_list.py

This file was deleted.

163 changes: 7 additions & 156 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from superannotate_schemas.schemas.classes import AnnotationClass
from superannotate_schemas.validators import AnnotationValidators
from superannotate_schemas.schemas.external.vector import Polygon


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

@patch('builtins.print')
def test_validate_vector_template_polygon_polyline_min_annotation(self, mock_print):
json_name = "test.json"
with tempfile.TemporaryDirectory() as tmpdir_name:
with open(f"{tmpdir_name}/{json_name}", "w") as json_file:
json_file.write(
'''
{
"metadata": {
"lastAction": {
"email": "some@some.com",
"timestamp": 1636964198056
},
"width": "1234",
"height": 1540,
"name": "t.png",
"projectId": 164988,
"isPredicted": false,
"status": "Completed",
"pinned": false,
"annotatorEmail": null,
"qaEmail": null
},
"comments": [],
"tags": [],
"instances": [
{
"type": "template",
"classId": 880080,
"probability": 100,
"points": [
],
"connections": [
{
"id": 1,
"from": 1,
"to": 2
}
],
"groupId": 0,
"pointLabels": {},
"locked": false,
"visible": true,
"attributes": [],
"templateId": 4728,
"trackingId": null,
"error": null,
"createdAt": "2021-11-15T08:24:40.712Z",
"createdBy": {
"email": "shab.prog@gmail.com",
"role": "Admin"
},
"creationType": "Manual",
"updatedAt": "2021-11-15T08:24:46.440Z",
"updatedBy": {
"email": "shab.prog@gmail.com",
"role": "Admin"
},
"className": "kj",
"templateName": "templ1"
},
{
"type": "polygon",
"classId": 880080,
"probability": 100,
"points": [
233.69
],
"groupId": 0,
"pointLabels": {},
"locked": true,
"visible": true,
"attributes": [],
"trackingId": null,
"error": null,
"createdAt": "2021-11-15T08:18:16.103Z",
"createdBy": {
"email": "some@some.com",
"role": "Admin"
},
"creationType": "Manual",
"updatedAt": "2021-11-15T08:18:20.233Z",
"updatedBy": {
"email": "some@some.com",
"role": "Admin"
},
"className": "kj"
},
{
"type": "polyline",
"classId": 880080,
"probability": 100,
"points": [
218.22
],
"groupId": 0,
"pointLabels": {},
"locked": false,
"visible": true,
"attributes": [],
"trackingId": null,
"error": null,
"createdAt": "2021-11-15T08:18:06.203Z",
"createdBy": {
"email": "some@some.com",
"role": "Admin"
},
"creationType": "Manual",
"updatedAt": "2021-11-15T08:18:13.439Z",
"updatedBy": {
"email": "some@some.com",
"role": "Admin"
},
"className": "kj"
},
{
"type": "bbox",
"classId": 880080,
"probability": 100,
"points": {
"x1": 487.78,
"x2": 1190.87,
"y1": 863.91,
"y2": 1463.78
},
"groupId": 0,
"pointLabels": {},
"locked": false,
"visible": true,
"attributes": [],
"trackingId": null,
"error": null,
"createdAt": "2021-11-15T06:43:09.812Z",
"createdBy": {
"email": "some@some.com",
"role": "Admin"
},
"creationType": "Manual",
"updatedAt": "2021-11-15T08:16:48.807Z",
"updatedBy": {
"email": "some@some.com",
"role": "Admin"
},
"className": "kj"
}
]
}
'''
)

with open(f"{tmpdir_name}/{json_name}", "r") as f:
data = json.loads(f.read())
validator = AnnotationValidators.get_validator("vector")(data)
self.assertFalse(validator.is_valid())
self.assertEqual(len(validator.generate_report()), 246)

def test_validate_video_point_labels(self):
with tempfile.TemporaryDirectory() as tmpdir_name:
with open(f"{tmpdir_name}/test_validate_video_point_labels.json",
Expand Down Expand Up @@ -2114,3 +1959,9 @@ def test_validate_tag_without_class_name(self):
self.assertFalse(validator.is_valid())
print(validator.generate_report())
self.assertEqual(len(validator.generate_report()), 191)

def test_strict_points(self):
try:
Polygon(points=["asd", 1, 1.0, 3], type="polygon")
except Exception as e:
print(333, e)