Skip to content

Commit 739a1c9

Browse files
authored
Merge pull request #423 from superannotateai/friday_580
Added query function.
2 parents 91f87e7 + 3cabbfb commit 739a1c9

File tree

16 files changed

+3213
-28
lines changed

16 files changed

+3213
-28
lines changed

docs/source/superannotate.sdk.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ ______
7777
.. _ref_search_images:
7878
.. autofunction:: superannotate.search_images
7979
.. autofunction:: superannotate.search_images_all_folders
80+
.. autofunction:: superannotate.query
8081
.. autofunction:: superannotate.get_image_metadata
8182
.. autofunction:: superannotate.download_image
8283
.. autofunction:: superannotate.set_image_annotation_status

src/superannotate/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
from superannotate.lib.app.interface.sdk_interface import move_images
7272
from superannotate.lib.app.interface.sdk_interface import pin_image
7373
from superannotate.lib.app.interface.sdk_interface import prepare_export
74+
from superannotate.lib.app.interface.sdk_interface import query
7475
from superannotate.lib.app.interface.sdk_interface import rename_project
7576
from superannotate.lib.app.interface.sdk_interface import run_prediction
7677
from superannotate.lib.app.interface.sdk_interface import search_annotation_classes
@@ -171,6 +172,8 @@
171172
"search_folders",
172173
"assign_folder",
173174
"unassign_folder",
175+
# Entities Section
176+
"query",
174177
# Image Section
175178
"copy_images",
176179
"move_images",

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from lib.infrastructure.repositories import ConfigRepository
2424

2525

26-
2726
class CLIFacade(BaseInterfaceFacade):
2827
"""
2928
With SuperAnnotate CLI, basic tasks can be accomplished using shell commands:

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2955,9 +2955,6 @@ def attach_items_from_integrated_storage(
29552955
:param project: project name or folder path where items should be attached (e.g., “project1/folder1”).
29562956
:type project: str
29572957
2958-
:param project: project name or folder path where items should be attached (e.g., “project1/folder1”).
2959-
:type project: str
2960-
29612958
:param integration: existing integration name or metadata dict to pull items from.
29622959
Mandatory keys in integration metadata’s dict is “name”.
29632960
:type integration: str or dict
@@ -2972,3 +2969,24 @@ def attach_items_from_integrated_storage(
29722969
response = Controller.get_default().attach_integrations(project_name, folder_name, integration, folder_path)
29732970
if response.errors:
29742971
raise AppException(response.errors)
2972+
2973+
2974+
@Trackable
2975+
@validate_arguments
2976+
def query(project: NotEmptyStr, query: Optional[NotEmptyStr]):
2977+
"""Return items
2978+
2979+
:param project: project name or folder path (e.g., “project1/folder1”)
2980+
:type project: str
2981+
2982+
:param query: SAQuL query string.
2983+
:type query: str
2984+
2985+
:return: queried items’ metadata list
2986+
:rtype: list of dicts
2987+
"""
2988+
project_name, folder_name = extract_project_folder(project)
2989+
response = Controller.get_default().query_entities(project_name, folder_name, query)
2990+
if response.errors:
2991+
raise AppException(response.errors)
2992+
return BaseSerializers.serialize_iterable(response.data)

src/superannotate/lib/app/serializers.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ class BaseSerializers(ABC):
1515
def __init__(self, entity: BaseEntity):
1616
self._entity = entity
1717

18+
@staticmethod
19+
def _fill_enum_values(data: dict):
20+
if isinstance(data, dict):
21+
for key, value in data.items():
22+
if hasattr(value, "_type") and value._type == "titled_enum":
23+
data[key] = value.__doc__
24+
return data
25+
1826
def serialize(self, fields: List[str] = None, by_alias: bool = True, flat: bool = False):
19-
return self._serialize(self._entity, fields, by_alias, flat)
27+
return self._fill_enum_values(self._serialize(self._entity, fields, by_alias, flat))
2028

2129
@staticmethod
2230
def _serialize(entity: Any, fields: List[str] = None, by_alias: bool = False, flat: bool = False):
@@ -43,7 +51,9 @@ def serialize_iterable(
4351
) -> List[Any]:
4452
serialized_data = []
4553
for i in data:
46-
serialized_data.append(cls._serialize(i, fields, by_alias, flat))
54+
serialized_data.append(
55+
cls._fill_enum_values(cls._serialize(i, fields, by_alias, flat))
56+
)
4757
return serialized_data
4858

4959

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
from typing import Any
44
from typing import Iterable
55
from typing import List
6+
from typing import Optional
67
from typing import Union
78

9+
from lib.core.enums import AnnotationStatus
810
from lib.core.enums import ClassTypeEnum
911
from lib.core.enums import SegmentationStatus
12+
from pydantic import BaseModel
13+
from pydantic import Field
1014
from superannotate_schemas.schemas.classes import AnnotationClass
1115

1216

@@ -477,3 +481,33 @@ def to_dict(self):
477481
"is_global": self.is_global,
478482
**self.hyper_parameters,
479483
}
484+
485+
486+
class Entity(BaseModel):
487+
id: int
488+
name: str
489+
path: Optional[str] = Field(None, description="Item’s path in SuperAnnotate project")
490+
url: Optional[str] = Field(None, description="Publicly available HTTP address")
491+
annotation_status: AnnotationStatus = Field(description="Item annotation status")
492+
annotator_name: Optional[str] = Field(description="Annotator email")
493+
qa_name: Optional[str] = Field(description="QA email")
494+
entropy_value: Optional[str] = Field(description="Priority score of given item")
495+
created_at: str = Field(alias="createdAt", description="Date of creation")
496+
updated_at: str = Field(alias="updatedAt", description="Update date")
497+
498+
class Config:
499+
ignore_extra = True
500+
501+
502+
class TmpImageEntity(Entity):
503+
prediction_status: Optional[SegmentationStatus] = Field(SegmentationStatus.NOT_STARTED)
504+
segmentation_status: Optional[SegmentationStatus] = Field(SegmentationStatus.NOT_STARTED)
505+
approval_status: bool = None
506+
507+
508+
class VideoEntity(Entity):
509+
pass
510+
511+
512+
class DocumentEntity(Entity):
513+
pass

src/superannotate/lib/core/enums.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,44 @@
22
from types import DynamicClassAttribute
33

44

5-
class BaseTitledEnum(Enum):
5+
class BaseTitledEnum(int, Enum):
6+
def __new__(cls, title, value):
7+
obj = int.__new__(cls, value)
8+
obj._value_ = value
9+
obj.__doc__ = title
10+
obj._type = "titled_enum"
11+
return obj
12+
613
@DynamicClassAttribute
714
def name(self) -> str:
8-
return super().value[0]
15+
return self.__doc__
916

1017
@DynamicClassAttribute
1118
def value(self):
12-
return super().value[1]
19+
return super().value
1320

1421
@classmethod
1522
def get_name(cls, value):
1623
for enum in list(cls):
1724
if enum.value == value:
18-
return enum.name
25+
return enum.__doc__
1926

2027
@classmethod
2128
def get_value(cls, name):
2229
for enum in list(cls):
23-
if enum.name.lower() == name.lower():
30+
if enum.__doc__.lower() == name.lower():
2431
return enum.value
2532

2633
@classmethod
2734
def values(cls):
28-
return [enum.name.lower() for enum in list(cls)]
35+
return [enum.__doc__.lower() for enum in list(cls)]
2936

3037
@classmethod
3138
def titles(cls):
32-
return [enum.name for enum in list(cls)]
39+
return [enum.__doc__ for enum in list(cls)]
3340

3441
def equals(self, other: Enum):
35-
return self.name.lower() == other.lower()
42+
return self.__doc__.lower() == other.__doc__.lower()
3643

3744

3845
class ProjectType(BaseTitledEnum):
@@ -92,9 +99,9 @@ class ClassTypeEnum(BaseTitledEnum):
9299
@classmethod
93100
def get_value(cls, name):
94101
for enum in list(cls):
95-
if enum.name.lower() == name.lower():
102+
if enum.__doc__.lower() == name.lower():
96103
return enum.value
97-
return "object"
104+
return cls.OBJECT.value
98105

99106

100107
class TrainingStatus(BaseTitledEnum):
@@ -113,7 +120,7 @@ class SegmentationStatus(BaseTitledEnum):
113120
FAILED = "Failed", 4
114121

115122

116-
class TrainingTask(BaseTitledEnum):
123+
class TrainingTask:
117124
INSTANCE_SEGMENTATION_PIXEL = (
118125
"Instance Segmentation for Pixel Projects",
119126
"instance_segmentation_pixel",

src/superannotate/lib/core/service_types.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,17 @@ class ServiceResponse(BaseModel):
7777
content: Union[bytes, str]
7878
data: Any
7979

80-
def __init__(self, response, content_type):
80+
def __init__(self, response, content_type=None):
8181
data = {
8282
"status": response.status_code,
8383
"reason": response.reason,
8484
"content": response.content,
8585
}
8686
if response.ok:
87-
data["data"] = content_type(**response.json())
87+
if content_type:
88+
data["data"] = content_type(**response.json())
89+
else:
90+
data["data"] = response.json()
8891
super().__init__(**data)
8992

9093
@property

src/superannotate/lib/core/serviceproviders.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,9 @@ def get_integrations(self, team_id: int) -> List[dict]:
326326
def attach_integrations(self, team_id: int, project_id: int, integration_id: int, folder_id: int,
327327
folder_name: str) -> bool:
328328
raise NotImplementedError
329+
330+
def saqul_query(self, team_id: int, project_id: int, folder_id: int, query: str) -> ServiceResponse:
331+
raise NotImplementedError
332+
333+
def validate_saqul_query(self, team_id: int, project_id: int, query: str) -> dict:
334+
raise NotImplementedError

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from lib.core.usecases.annotations import * # noqa: F403 F401
2+
from lib.core.usecases.entities import * # noqa: F403 F401
23
from lib.core.usecases.folders import * # noqa: F403 F401
34
from lib.core.usecases.images import * # noqa: F403 F401
45
from lib.core.usecases.integrations import * # noqa: F403 F401

0 commit comments

Comments
 (0)