Skip to content

Commit 5c7b702

Browse files
authored
Merge pull request #476 from superannotateai/1089_getting_custom_values
added custom fields
2 parents 3788191 + 52c70c8 commit 5c7b702

File tree

14 files changed

+245
-85
lines changed

14 files changed

+245
-85
lines changed

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

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,23 +2318,59 @@ def get_item_metadata(
23182318
self,
23192319
project: NotEmptyStr,
23202320
item_name: NotEmptyStr,
2321+
include_custom_metadata: bool = False,
23212322
):
23222323
"""Returns item metadata
23232324
23242325
:param project: project name or folder path (e.g., “project1/folder1”)
23252326
:type project: str
23262327
2327-
:param item_name: item name
2328+
:param item_name: item name.
23282329
:type item_name: str
23292330
2331+
:param include_custom_metadata: include custom metadata that has been attached to an asset.
2332+
:type include_custom_metadata: bool
2333+
23302334
:return: metadata of item
23312335
:rtype: dict
2336+
2337+
Request Example:
2338+
::
2339+
client.get_item_metadata(
2340+
project="Medical Annotations",
2341+
item_name = "image_1.png",
2342+
include_custom_metadata=True
2343+
)
2344+
2345+
2346+
Response Example:
2347+
::
2348+
{
2349+
"name": "image_1.jpeg",
2350+
"path": "Medical Annotations/Study",
2351+
"url": "https://sa-public-files.s3.../image_1.png",
2352+
"annotation_status": "NotStarted",
2353+
"annotator_email": None,
2354+
"qa_email": None,
2355+
"entropy_value": None,
2356+
"createdAt": "2022-02-15T20:46:44.000Z",
2357+
"updatedAt": "2022-02-15T20:46:44.000Z",
2358+
"custom_metadata": {
2359+
"study_date": "2021-12-31",
2360+
"patient_id": "62078f8a756ddb2ca9fc9660",
2361+
"patient_sex": "female",
2362+
"medical_specialist": "robertboxer@ms.com",
2363+
}
2364+
}
23322365
"""
23332366
project_name, folder_name = extract_project_folder(project)
2334-
response = self.controller.get_item(project_name, folder_name, item_name)
2367+
response = self.controller.get_item(
2368+
project_name, folder_name, item_name, include_custom_metadata
2369+
)
2370+
exclude = {"custom_metadata"} if not include_custom_metadata else {}
23352371
if response.errors:
23362372
raise AppException(response.errors)
2337-
return BaseSerializer(response.data).serialize()
2373+
return BaseSerializer(response.data).serialize(exclude=exclude)
23382374

23392375
def search_items(
23402376
self,
@@ -2344,6 +2380,7 @@ def search_items(
23442380
annotator_email: Optional[NotEmptyStr] = None,
23452381
qa_email: Optional[NotEmptyStr] = None,
23462382
recursive: bool = False,
2383+
include_custom_metadata: bool = False,
23472384
):
23482385
"""Search items by filtering criteria.
23492386
@@ -2365,8 +2402,6 @@ def search_items(
23652402
♦ “Completed” \n
23662403
♦ “Skippe
23672404
:type annotation_status: str
2368-
:type annotation_status: str
2369-
23702405
23712406
:param annotator_email: returns those items’ names that are assigned to the specified annotator.
23722407
If None, all items are returned. Strict equal.
@@ -2380,8 +2415,41 @@ def search_items(
23802415
If False search only in the project’s root or given directory.
23812416
:type recursive: bool
23822417
2383-
:return: items' metadata
2418+
:param include_custom_metadata: include custom metadata that has been attached to an asset.
2419+
:type include_custom_metadata: bool
2420+
2421+
:return: metadata of item
23842422
:rtype: list of dicts
2423+
2424+
Request Example:
2425+
::
2426+
client.search_items(
2427+
project="Medical Annotations",
2428+
name_contains="image_1",
2429+
include_custom_metadata=True
2430+
)
2431+
2432+
Response Example:
2433+
::
2434+
[
2435+
{
2436+
"name": "image_1.jpeg",
2437+
"path": "Medical Annotations/Study",
2438+
"url": "https://sa-public-files.s3.../image_1.png",
2439+
"annotation_status": "NotStarted",
2440+
"annotator_email": None,
2441+
"qa_email": None,
2442+
"entropy_value": None,
2443+
"createdAt": "2022-02-15T20:46:44.000Z",
2444+
"updatedAt": "2022-02-15T20:46:44.000Z",
2445+
"custom_metadata": {
2446+
"study_date": "2021-12-31",
2447+
"patient_id": "62078f8a756ddb2ca9fc9660",
2448+
"patient_sex": "female",
2449+
"medical_specialist": "robertboxer@ms.com",
2450+
}
2451+
}
2452+
]
23852453
"""
23862454
project_name, folder_name = extract_project_folder(project)
23872455
response = self.controller.list_items(
@@ -2392,10 +2460,12 @@ def search_items(
23922460
annotator_email=annotator_email,
23932461
qa_email=qa_email,
23942462
recursive=recursive,
2463+
include_custom_metadata=include_custom_metadata,
23952464
)
2465+
exclude = {"custom_metadata"} if not include_custom_metadata else {}
23962466
if response.errors:
23972467
raise AppException(response.errors)
2398-
return BaseSerializer.serialize_iterable(response.data)
2468+
return BaseSerializer.serialize_iterable(response.data, exclude=exclude)
23992469

24002470
def attach_items(
24012471
self,

src/superannotate/lib/app/serializers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,14 @@ def serialize_iterable(
7373
fields: Union[List[str], Set[str]] = None,
7474
by_alias: bool = False,
7575
flat: bool = False,
76+
exclude: Set = None,
7677
) -> List[Any]:
7778
serialized_data = []
7879
for i in data:
7980
serialized_data.append(
80-
cls._fill_enum_values(cls._serialize(i, fields, by_alias, flat))
81+
cls._fill_enum_values(
82+
cls._serialize(i, fields, by_alias, flat, exclude=exclude)
83+
)
8184
)
8285
return serialized_data
8386

src/superannotate/lib/core/__init__.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212
from superannotate.lib.core.enums import UploadState
1313
from superannotate.lib.core.enums import UserRole
1414

15-
1615
CONFIG = Config()
1716

18-
1917
CONFIG_PATH = "~/.superannotate/config.json"
2018
CONFIG_FILE_LOCATION = expanduser(CONFIG_PATH)
2119
LOG_FILE_LOCATION = expanduser("~/.superannotate/sa.log")
@@ -49,7 +47,6 @@
4947

5048
AVAILABLE_SEGMENTATION_MODELS = ["autonomous", "generic"]
5149

52-
5350
VECTOR_ANNOTATION_POSTFIX = "___objects.json"
5451
PIXEL_ANNOTATION_POSTFIX = "___pixel.json"
5552
ANNOTATION_MASK_POSTFIX = "___save.png"
@@ -86,6 +83,9 @@
8683
ProjectType.DOCUMENT.value: DEPRECATED_DOCUMENT_PROJECTS_MESSAGE,
8784
}
8885

86+
METADATA_DEPRICATED_FOR_PIXEL = (
87+
"custom_metadata field is not supported for project type Pixel."
88+
)
8989
DEPRICATED_DOCUMENT_VIDEO_MESSAGE = "The function does not support projects containing videos / documents attached with URLs"
9090

9191
UPLOAD_FOLDER_LIMIT_ERROR_MESSAGE = "The number of items you want to upload exceeds the limit of 50 000 items per folder."
@@ -96,7 +96,6 @@
9696
ATTACH_PROJECT_LIMIT_ERROR_MESSAGE = "The number of items you want to attach exceeds the limit of 500 000 items per project."
9797
ATTACH_USER_LIMIT_ERROR_MESSAGE = "The number of items you want to attach exceeds the limit of your subscription plan."
9898

99-
10099
COPY_FOLDER_LIMIT_ERROR_MESSAGE = (
101100
"The number of items you want to copy exceeds the limit of 50 000 items per folder."
102101
)
@@ -105,7 +104,6 @@
105104
"The number of items you want to copy exceeds the limit of your subscription plan."
106105
)
107106

108-
109107
MOVE_FOLDER_LIMIT_ERROR_MESSAGE = (
110108
"The number of items you want to move exceeds the limit of 50 000 items per folder."
111109
)

src/superannotate/lib/core/service_types.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Any
2+
from typing import Callable
23
from typing import Dict
34
from typing import List
45
from typing import Optional
@@ -79,30 +80,55 @@ class ServiceResponse(BaseModel):
7980
reason: str
8081
content: Union[bytes, str]
8182
data: Any
83+
count: Optional[int] = 0
84+
_error: str
8285

83-
def __init__(self, response, content_type=None):
86+
class Config:
87+
extra = Extra.allow
88+
89+
def __init__(self, response, content_type=None, dispatcher: Callable = None):
8490
data = {
8591
"status": response.status_code,
8692
"reason": response.reason,
8793
"content": response.content,
8894
}
95+
try:
96+
response_json = response.json()
97+
except Exception:
98+
response_json = dict()
99+
if not response.ok:
100+
data["_error"] = response_json.get("error", "Unknown Error")
101+
data["data"] = response_json
102+
super().__init__(**data)
103+
return
104+
if dispatcher:
105+
_data = response_json
106+
response_json = dispatcher(_data)
107+
data.update(_data)
89108
try:
90109
if content_type and content_type is not self.__class__:
91-
data["data"] = parse_obj_as(content_type, response.json())
110+
data["data"] = parse_obj_as(content_type, response_json)
92111
else:
93-
data["data"] = response.json()
94-
except Exception as e:
112+
data["data"] = response_json
113+
except Exception:
95114
data["data"] = {}
115+
96116
super().__init__(**data)
97117

118+
@property
119+
def status_code(self):
120+
return self.status
121+
98122
@property
99123
def ok(self):
100124
return 199 < self.status < 300
101125

102126
@property
103127
def error(self):
128+
if self._error:
129+
return self._error
104130
default_message = self.reason if self.reason else "Unknown Error"
105-
if isinstance(self.data, dict):
131+
if isinstance(self.data, dict) and "error" in self.data:
106132
return self.data.get("error", default_message)
107133
else:
108134
return getattr(self.data, "error", default_message)

src/superannotate/lib/core/serviceproviders.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ def create_folder(self, project_id: int, team_id: int, folder_name: str):
9595
def update_folder(self, project_id: int, team_id: int, folder_data: dict):
9696
raise NotImplementedError
9797

98+
def list_items(self, query_params: str) -> ServiceResponse:
99+
raise NotImplementedError
100+
98101
def get_download_token(
99102
self,
100103
project_id: int,

0 commit comments

Comments
 (0)