diff --git a/pytest.ini b/pytest.ini index d9ab3b4..de4eaf7 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,4 +2,4 @@ minversion = 3.0 log_cli=true python_files = test_*.py -;addopts = -n auto --dist=loadscope \ No newline at end of file +addopts = -n auto --dist=loadscope diff --git a/src/superannotate_schemas/schemas/base.py b/src/superannotate_schemas/schemas/base.py index 8794bfe..baed528 100644 --- a/src/superannotate_schemas/schemas/base.py +++ b/src/superannotate_schemas/schemas/base.py @@ -26,6 +26,7 @@ from superannotate_schemas.schemas.enums import BaseImageRoleEnum from superannotate_schemas.schemas.enums import VectorAnnotationTypeEnum from superannotate_schemas.schemas.enums import AnnotationStatusEnum +from superannotate_schemas.schemas.enums import TagTypeEnum from superannotate_schemas.schemas.constances import DATE_REGEX from superannotate_schemas.schemas.constances import DATE_TIME_FORMAT_ERROR_MESSAGE from superannotate_schemas.schemas.constances import POINT_LABEL_VALUE_FORMAT_ERROR_MESSAGE @@ -64,20 +65,14 @@ class AxisPoint(BaseModel): class BaseAttribute(BaseModel): id: Optional[StrictInt] group_id: Optional[StrictInt] = Field(alias="groupId") - name: Optional[NotEmptyStr] - group_name: Optional[NotEmptyStr] = Field(alias="groupName") + name: NotEmptyStr + group_name: NotEmptyStr = Field(alias="groupName") class Tag(BaseModel): __root__: NotEmptyStr -class AttributeGroup(BaseModel): - name: NotEmptyStr - is_multiselect: Optional[bool] = Field(False) - attributes: List[BaseAttribute] - - class BboxPoints(BaseModel): x1: StrictNumber x2: StrictNumber @@ -126,6 +121,12 @@ class BaseInstance(TrackableModel, TimedBaseModel): class_name: Optional[NotEmptyStr] = Field(None, alias="className") +class BaseInstanceTag(BaseInstance): + type: TagTypeEnum + probability: Optional[StrictInt] = Field(100) + attributes: Optional[List[BaseAttribute]] = Field(list()) + + class BaseMetadata(BaseModel): name: NotEmptyStr url: Optional[StrictStr] @@ -139,7 +140,7 @@ class BaseMetadata(BaseModel): class BaseImageMetadata(BaseMetadata): width: Optional[StrictInt] height: Optional[StrictInt] - pinned: Optional[bool] + pinned: Optional[StrictBool] class Correspondence(BaseModel): @@ -220,13 +221,6 @@ class BaseVectorInstance(BaseImageAnnotationInstance): tracking_id: Optional[str] = Field(alias="trackingId") group_id: Optional[int] = Field(alias="groupId") -# -# class Metadata(BaseMetadata): -# name: NotEmptyStr -# status: Optional[AnnotationStatusEnum] -# pinned: Optional[StrictBool] -# is_predicted: Optional[StrictBool] = Field(None, alias="isPredicted") - class PixelColor(BaseModel): __root__: ColorType diff --git a/src/superannotate_schemas/schemas/enums.py b/src/superannotate_schemas/schemas/enums.py index 0b8557b..e8ccdbb 100644 --- a/src/superannotate_schemas/schemas/enums.py +++ b/src/superannotate_schemas/schemas/enums.py @@ -10,6 +10,7 @@ class VectorAnnotationTypeEnum(str, Enum): POLYGON = "polygon" POINT = "point" RBBOX = "rbbox" + TAG = "tag" class CreationTypeEnum(str, Enum): @@ -38,3 +39,8 @@ class BaseImageRoleEnum(str, Enum): ADMIN = "Admin" ANNOTATOR = "Annotator" QA = "QA" + + +class TagTypeEnum(str, Enum): + TAG = "tag" + OBJECT = "object" diff --git a/src/superannotate_schemas/schemas/external/vector.py b/src/superannotate_schemas/schemas/external/vector.py index 637faca..d04be70 100644 --- a/src/superannotate_schemas/schemas/external/vector.py +++ b/src/superannotate_schemas/schemas/external/vector.py @@ -21,9 +21,14 @@ from superannotate_schemas.schemas.base import NotEmptyStr from superannotate_schemas.schemas.base import StrictNumber from superannotate_schemas.schemas.base import Tag +from superannotate_schemas.schemas.base import BaseInstanceTag from superannotate_schemas.schemas.enums import VectorAnnotationTypeEnum +class InstanceTag(BaseInstanceTag): + class_name: NotEmptyStr + + class Attribute(BaseAttribute): name: NotEmptyStr group_name: NotEmptyStr = Field(alias="groupName") @@ -115,12 +120,13 @@ class Cuboid(VectorInstance): VectorAnnotationTypeEnum.POLYLINE: PolyLine, VectorAnnotationTypeEnum.ELLIPSE: Ellipse, VectorAnnotationTypeEnum.RBBOX: RotatedBox, + VectorAnnotationTypeEnum.TAG: InstanceTag, } class AnnotationInstance(BaseModel): __root__: Union[ - Template, Cuboid, Point, PolyLine, Polygon, Bbox, Ellipse, RotatedBox + Template, Cuboid, Point, PolyLine, Polygon, Bbox, Ellipse, RotatedBox, InstanceTag ] @classmethod diff --git a/src/superannotate_schemas/schemas/internal/vector.py b/src/superannotate_schemas/schemas/internal/vector.py index 4ec86bb..16cc89b 100644 --- a/src/superannotate_schemas/schemas/internal/vector.py +++ b/src/superannotate_schemas/schemas/internal/vector.py @@ -10,6 +10,7 @@ from superannotate_schemas.schemas.base import Tag from superannotate_schemas.schemas.base import AxisPoint from superannotate_schemas.schemas.base import VectorAnnotationTypeEnum +from superannotate_schemas.schemas.base import BaseInstanceTag from superannotate_schemas.schemas.base import StrictNumber from superannotate_schemas.schemas.base import INVALID_DICT_MESSAGE from superannotate_schemas.schemas.base import BaseModel @@ -24,6 +25,11 @@ from pydantic.error_wrappers import ErrorWrapper + +class InstanceTag(BaseInstanceTag): + class_id: StrictInt + + class Attribute(BaseAttribute): id: StrictInt group_id: StrictInt = Field(alias="groupId") @@ -110,11 +116,13 @@ class Cuboid(VectorInstance): VectorAnnotationTypeEnum.POLYLINE: PolyLine, VectorAnnotationTypeEnum.ELLIPSE: Ellipse, VectorAnnotationTypeEnum.RBBOX: RotatedBox, + VectorAnnotationTypeEnum.TAG: InstanceTag, } + class AnnotationInstance(BaseModel): __root__: Union[ - Template, Cuboid, Point, PolyLine, Polygon, Bbox, Ellipse, RotatedBox + Template, Cuboid, Point, PolyLine, Polygon, Bbox, Ellipse, RotatedBox, InstanceTag ] @classmethod diff --git a/tests/test_cli.py b/tests/test_cli.py index c968554..5b995e8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -82,4 +82,5 @@ def test_(self): # pass # print(time.time() - s) import datetime - print((datetime.datetime.now(datetime.timezone.utc)).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + 'Z') \ No newline at end of file + print((datetime.datetime.now(datetime.timezone.utc)).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + 'Z') + diff --git a/tests/test_validators.py b/tests/test_validators.py index 939a233..facd2cd 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -310,7 +310,12 @@ def test_validate_document_annotation_wrong_class_id(self, mock_print): "email": "some.email@gmail.com", "role": "Admin" }, - "attributes": [], + "attributes": [ + { + "id": 1175876, + "groupId": 338357 + } + ], "creationType": "Manual", "className": "vid" }], @@ -324,8 +329,7 @@ def test_validate_document_annotation_wrong_class_id(self, mock_print): data = json.loads(f.read()) validator = AnnotationValidators.get_validator("document")(data) self.assertFalse(validator.is_valid()) - self.assertEqual(validator.generate_report(), - "instances[0].classId integer type expected") + self.assertEqual(len(validator.generate_report()), 198) def test_validate_document_annotation_with_null_created_at(self): with tempfile.TemporaryDirectory() as tmpdir_name: @@ -2147,11 +2151,11 @@ def test_validate_vector_empty_annotation_bad_role(self): }, "width":480, "height":270, + "pinned": [ "fasdf" ], "name":"1 copy_001.jpg", "projectId":181302, "isPredicted":false, "status":"Completed", - "pinned":false, "annotatorEmail":null, "qaEmail":null }, @@ -2209,4 +2213,5 @@ def test_validate_vector_empty_annotation_bad_role(self): data = json.loads(f.read()) validator = AnnotationValidators.get_validator("vector")(data) self.assertFalse(validator.is_valid()) - self.assertEqual(len(validator.generate_report()), 113) \ No newline at end of file + print(validator.generate_report()) + self.assertEqual(len(validator.generate_report()), 191) \ No newline at end of file