Skip to content

Commit bd2d794

Browse files
authored
Merge pull request #544 from superannotateai/get_things_by_id
Get things by
2 parents 6fc1ba8 + adf6f16 commit bd2d794

File tree

23 files changed

+376
-78
lines changed

23 files changed

+376
-78
lines changed

docs/source/superannotate.sdk.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ ________
2525
.. automethod:: superannotate.SAClient.delete_project
2626
.. automethod:: superannotate.SAClient.rename_project
2727
.. _ref_get_project_metadata:
28+
.. automethod:: superannotate.SAClient.get_project_by_id
2829
.. automethod:: superannotate.SAClient.get_project_metadata
2930
.. automethod:: superannotate.SAClient.get_project_image_count
3031
.. automethod:: superannotate.SAClient.search_folders
3132
.. automethod:: superannotate.SAClient.assign_folder
3233
.. automethod:: superannotate.SAClient.unassign_folder
34+
.. automethod:: superannotate.SAClient.get_folder_by_id
3335
.. automethod:: superannotate.SAClient.get_folder_metadata
3436
.. automethod:: superannotate.SAClient.create_folder
3537
.. automethod:: superannotate.SAClient.delete_folders
@@ -68,6 +70,7 @@ Items
6870
______
6971

7072
.. automethod:: superannotate.SAClient.query
73+
.. automethod:: superannotate.SAClient.get_item_by_id
7174
.. automethod:: superannotate.SAClient.search_items
7275
.. automethod:: superannotate.SAClient.download_annotations
7376
.. automethod:: superannotate.SAClient.attach_items

src/superannotate/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import sys
33

44

5-
__version__ = "4.4.8dev1"
5+
__version__ = "4.4.8dev2"
6+
67

78

89
sys.path.append(os.path.split(os.path.realpath(__file__))[0])

src/superannotate/lib/app/analytics/aggregators.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
import lib.core as constances
1010
import pandas as pd
1111
from lib.app.exceptions import AppException
12-
from lib.core import ATTACHED_VIDEO_ANNOTATION_POSTFIX
1312
from lib.core import PIXEL_ANNOTATION_POSTFIX
1413
from lib.core import VECTOR_ANNOTATION_POSTFIX
1514
from superannotate.logger import get_default_logger
1615

1716
logger = get_default_logger()
1817

18+
1919
@dataclass
2020
class ImageRowData:
2121
itemName: str = None
@@ -50,6 +50,7 @@ class ImageRowData:
5050
commentResolved: str = None
5151
tag: str = None
5252

53+
5354
@dataclass
5455
class VideoRawData:
5556
itemName: str = None
@@ -135,9 +136,9 @@ class DataAggregator:
135136
),
136137
"tag": lambda annotation: None,
137138
"mask": lambda annotation: {"parts": annotation["parts"]},
138-
"template": lambda annotation : None,
139+
"template": lambda annotation: None,
139140
"rbbox": lambda annotation: annotation["points"],
140-
"comment_inst": lambda annotation: annotation["points"]
141+
"comment_inst": lambda annotation: annotation["points"],
141142
}
142143

143144
def __init__(
@@ -204,18 +205,16 @@ def aggregate_annotations_as_df(self):
204205
self.check_classes_path()
205206
annotation_paths = self.get_annotation_paths()
206207

207-
208208
if self.project_type in (
209209
constances.ProjectType.VECTOR,
210-
constances.ProjectType.PIXEL
210+
constances.ProjectType.PIXEL,
211211
):
212212
return self.aggregate_image_annotations_as_df(annotation_paths)
213213
elif self.project_type is constances.ProjectType.VIDEO:
214214
return self.aggregate_video_annotations_as_df(annotation_paths)
215215
elif self.project_type is constances.ProjectType.DOCUMENT:
216216
return self.aggregate_document_annotations_as_df(annotation_paths)
217217

218-
219218
def __add_attributes_to_raws(self, raws, attributes, element_raw):
220219
for attribute_id, attribute in enumerate(attributes):
221220
attribute_raw = copy.copy(element_raw)
@@ -388,10 +387,10 @@ def aggregate_image_annotations_as_df(self, annotations_paths: List[str]):
388387
for annotation_path in annotations_paths:
389388
row_data = ImageRowData()
390389
annotation_json = None
391-
with open(annotation_path, 'r') as fp:
390+
with open(annotation_path) as fp:
392391
annotation_json = json.load(fp)
393392
parts = Path(annotation_path).name.split(self._annotation_suffix)
394-
row_data = self.__fill_image_metadata(row_data, annotation_json['metadata'])
393+
row_data = self.__fill_image_metadata(row_data, annotation_json["metadata"])
395394
annotation_instance_id = 0
396395

397396
# include comments
@@ -408,7 +407,7 @@ def aggregate_image_annotations_as_df(self, annotations_paths: List[str]):
408407
tag_row.rag = tag
409408
rows.append(tag_row)
410409

411-
#Instances
410+
# Instances
412411
for idx, annotation in enumerate(annotation_json["instances"]):
413412
instance_row = copy.copy(row_data)
414413
annotation_type = annotation.get("type", "mask")
@@ -462,7 +461,8 @@ def aggregate_image_annotations_as_df(self, annotations_paths: List[str]):
462461
attribute_name
463462
not in class_group_name_to_values[annotation_class_name][
464463
attribute_group
465-
] and group_id not in freestyle_attributes
464+
]
465+
and group_id not in freestyle_attributes
466466
):
467467
logger.warning(
468468
f"Annotation class group value {attribute_name} not in classes json. Skipping."
@@ -473,7 +473,6 @@ def aggregate_image_annotations_as_df(self, annotations_paths: List[str]):
473473
attribute_row.attributeGroupName = attribute_group
474474
attribute_row.attributeName = attribute_name
475475

476-
477476
rows.append(attribute_row)
478477
num_added += 1
479478

@@ -486,7 +485,7 @@ def aggregate_image_annotations_as_df(self, annotations_paths: List[str]):
486485

487486
@staticmethod
488487
def __fill_image_metadata(raw_data, metadata):
489-
raw_data.itemName = metadata.get('name')
488+
raw_data.itemName = metadata.get("name")
490489
raw_data.itemHeight = metadata.get("height")
491490
raw_data.itemWidth = metadata.get("width")
492491
raw_data.itemStatus = metadata.get("status")

src/superannotate/lib/app/analytics/common.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ def instance_consensus(inst_1, inst_2):
398398

399399
return score
400400

401+
401402
def calculate_tag_consensus(image_df):
402403
column_names = [
403404
"creatorEmail",
@@ -406,14 +407,14 @@ def calculate_tag_consensus(image_df):
406407
"className",
407408
"folderName",
408409
"attributeGroupName",
409-
"attributeName"
410+
"attributeName",
410411
]
411412

412413
image_data = {}
413414
for column_name in column_names:
414415
image_data[column_name] = []
415416

416-
image_df=image_df.reset_index()
417+
image_df = image_df.reset_index()
417418
image_data["score"] = []
418419
for i, irow in image_df.iterrows():
419420
for c in column_names:
@@ -422,10 +423,15 @@ def calculate_tag_consensus(image_df):
422423
for j, jrow in image_df.iterrows():
423424
if i == j:
424425
continue
425-
if (irow["className"] == jrow["className"]) and irow["attributeGroupName"] == jrow["attributeGroupName"] and irow["attributeName"] == jrow["attributeName"]:
426-
image_data["score"][i]+=1
426+
if (
427+
(irow["className"] == jrow["className"])
428+
and irow["attributeGroupName"] == jrow["attributeGroupName"]
429+
and irow["attributeName"] == jrow["attributeName"]
430+
):
431+
image_data["score"][i] += 1
427432
return image_data
428433

434+
429435
def consensus(df, item_name, annot_type):
430436
"""Helper function that computes consensus score for instances of a single image:
431437
@@ -484,7 +490,7 @@ def consensus(df, item_name, annot_type):
484490
inst = Polygon(shapely_format)
485491
elif annot_type == "point":
486492
inst = Point(inst_data["x"], inst_data["y"])
487-
if annot_type != "tag" and inst.is_valid:
493+
if annot_type != "tag" and inst.is_valid:
488494
projects_shaply_objs[row["folderName"]].append(
489495
(inst, row["className"], row["creatorEmail"], row["attributes"])
490496
)

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

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from lib.app.interface.types import Setting
3636
from lib.app.serializers import BaseSerializer
3737
from lib.app.serializers import FolderSerializer
38+
from lib.app.serializers import ItemSerializer
3839
from lib.app.serializers import ProjectSerializer
3940
from lib.app.serializers import SettingsSerializer
4041
from lib.app.serializers import TeamSerializer
@@ -85,6 +86,60 @@ def __init__(
8586
):
8687
super().__init__(token, config_path)
8788

89+
def get_project_by_id(self, project_id: int):
90+
"""Returns the project metadata
91+
92+
:param project_id: the id of the project
93+
:type project_id: int
94+
95+
:return: project metadata
96+
:rtype: dict
97+
"""
98+
response = self.controller.get_project_by_id(project_id=project_id)
99+
100+
return ProjectSerializer(response.data).serialize()
101+
102+
def get_folder_by_id(self, project_id: int, folder_id: int):
103+
"""Returns the folder metadata
104+
105+
:param project_id: the id of the project
106+
:type project_id: int
107+
108+
:param folder_id: the id of the folder
109+
:type folder_id: int
110+
111+
:return: folder metadata
112+
:rtype: dict
113+
"""
114+
115+
response = self.controller.get_folder_by_id(
116+
folder_id=folder_id, project_id=project_id
117+
)
118+
119+
return FolderSerializer(response).serialize(
120+
exclude={"completedCount", "is_root"}
121+
)
122+
123+
def get_item_by_id(self, project_id: int, item_id: int):
124+
"""Returns the item metadata
125+
126+
:param project_id: the id of the project
127+
:type project_id: int
128+
129+
:param item_id: the id of the item
130+
:type item_id: int
131+
132+
133+
:return: item metadata
134+
:rtype: dict
135+
"""
136+
137+
response = self.controller.get_item_by_id(
138+
item_id=item_id, project_id=project_id
139+
)
140+
141+
return ItemSerializer(response).serialize(exclude={"url", "meta"})
142+
88143
def get_team_metadata(self):
89144
"""Returns team metadata
90145
@@ -214,7 +269,7 @@ def create_project(
214269
ProjectTypes.validate(project_type)
215270
except TypeError:
216271
raise AppException(
217-
"Please provide a valid project type: Vector, Pixel, Document, or Video."
272+
f"Please provide a valid project type: {', '.join(constants.ProjectType.titles())}"
218273
)
219274
response = self.controller.projects.create(
220275
entities.ProjectEntity(
@@ -1563,10 +1618,7 @@ def upload_image_annotations(
15631618
project_name, folder_name = extract_project_folder(project)
15641619

15651620
project = self.controller.projects.get_by_name(project_name).data
1566-
if project.type in [
1567-
constants.ProjectType.VIDEO,
1568-
constants.ProjectType.DOCUMENT,
1569-
]:
1621+
if project.type not in constants.ProjectType.images:
15701622
raise AppException(LIMITED_FUNCTIONS[project.type])
15711623

15721624
if not mask:
@@ -1659,10 +1711,7 @@ def benchmark(
16591711
project_name = project["name"]
16601712

16611713
project = self.controller.projects.get_by_name(project_name).data
1662-
if project.type in [
1663-
constants.ProjectType.VIDEO,
1664-
constants.ProjectType.DOCUMENT,
1665-
]:
1714+
if project.type not in constants.ProjectType.images:
16661715
raise AppException(LIMITED_FUNCTIONS[project.type])
16671716

16681717
if not export_root:

src/superannotate/lib/app/serializers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,19 @@ def serialize(self):
166166
return self.data
167167

168168

169+
class ItemSerializer(BaseSerializer):
170+
def serialize(
171+
self,
172+
fields: List[str] = None,
173+
by_alias: bool = False,
174+
flat: bool = False,
175+
exclude: Set[str] = None,
176+
):
177+
data = super().serialize(fields, by_alias, flat, exclude)
178+
179+
return data
180+
181+
169182
class EntitySerializer:
170183
@classmethod
171184
def serialize(

src/superannotate/lib/core/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@
5050
DEPRECATED_DOCUMENT_PROJECTS_MESSAGE = (
5151
"The function does not support projects containing documents attached with URLs"
5252
)
53+
DEPRECATED_PROJECTS_MESSAGE = (
54+
"The function does not support projects containing items attached with URLs"
55+
)
5356

5457
LIMITED_FUNCTIONS = {
5558
ProjectType.VIDEO: DEPRECATED_VIDEO_PROJECTS_MESSAGE,
5659
ProjectType.DOCUMENT: DEPRECATED_DOCUMENT_PROJECTS_MESSAGE,
60+
ProjectType.OTHER: DEPRECATED_PROJECTS_MESSAGE,
61+
ProjectType.POINT_CLOUD: DEPRECATED_PROJECTS_MESSAGE,
5762
}
5863

5964
METADATA_DEPRICATED_FOR_PIXEL = (

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
from lib.core.entities.classes import AnnotationClassEntity
44
from lib.core.entities.folder import FolderEntity
55
from lib.core.entities.integrations import IntegrationEntity
6+
from lib.core.entities.items import ClassificationEntity
67
from lib.core.entities.items import DocumentEntity
78
from lib.core.entities.items import ImageEntity
9+
from lib.core.entities.items import PointCloudEntity
10+
from lib.core.entities.items import TiledEntity
811
from lib.core.entities.items import VideoEntity
912
from lib.core.entities.project import AttachmentEntity
1013
from lib.core.entities.project import MLModelEntity
@@ -27,6 +30,9 @@
2730
"ImageEntity",
2831
"BaseItemEntity",
2932
"VideoEntity",
33+
"PointCloudEntity",
34+
"TiledEntity",
35+
"ClassificationEntity",
3036
"DocumentEntity",
3137
# Utils
3238
"AttachmentEntity",

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,18 @@ class Config:
3232
class DocumentEntity(BaseItemEntity):
3333
class Config:
3434
extra = Extra.ignore
35+
36+
37+
class TiledEntity(BaseItemEntity):
38+
class Config:
39+
extra = Extra.ignore
40+
41+
42+
class ClassificationEntity(BaseItemEntity):
43+
class Config:
44+
extra = Extra.ignore
45+
46+
47+
class PointCloudEntity(BaseItemEntity):
48+
class Config:
49+
extra = Extra.ignore

src/superannotate/lib/core/enums.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from types import DynamicClassAttribute
33

44

5-
class classproperty:
5+
class classproperty: # noqa
66
def __init__(self, getter):
77
self.getter = getter
88

@@ -78,14 +78,12 @@ class ProjectType(BaseTitledEnum):
7878
VIDEO = "Video", 3
7979
DOCUMENT = "Document", 4
8080
TILED = "Tiled", 5
81-
CLASSIFICATION = "Classification", 6
82-
POINT_CLOUD = "Point Cloud", 7
83-
84-
81+
OTHER = "Other", 6
82+
POINT_CLOUD = "PointCloud", 7
8583

8684
@classproperty
8785
def images(self):
88-
return self.VECTOR.value, self.PIXEL.value
86+
return self.VECTOR.value, self.PIXEL.value, self.TILED.value
8987

9088

9189
class UserRole(BaseTitledEnum):

0 commit comments

Comments
 (0)