Skip to content

Commit f642f77

Browse files
committed
More folder structure support
1 parent 6e03bac commit f642f77

File tree

11 files changed

+300
-219
lines changed

11 files changed

+300
-219
lines changed

superannotate/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ def consensus(*args, **kwargs):
4242
from .db.exports import (
4343
download_export, get_export_metadata, get_exports, prepare_export
4444
)
45-
from .db.folders import create_folder, get_folder_metadata, search_folders
4645
from .db.images import (
4746
add_annotation_bbox_to_image, add_annotation_comment_to_image,
4847
add_annotation_cuboid_to_image, add_annotation_ellipse_to_image,
@@ -53,6 +52,7 @@ def consensus(*args, **kwargs):
5352
get_image_metadata, get_image_preannotations, search_images,
5453
set_image_annotation_status, upload_image_annotations
5554
)
55+
from .db.project_api import create_folder, get_folder_metadata, search_folders
5656
from .db.project_images import (
5757
assign_images, copy_image, move_image, pin_image, upload_image_to_project
5858
)

superannotate/db/clone_project.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from .projects import create_project_from_metadata
1010

1111
logger = logging.getLogger("superannotate-python-sdk")
12-
_api = API.get_instance()
1312

1413

1514
def clone_project(

superannotate/db/folders.py

Lines changed: 0 additions & 95 deletions
This file was deleted.

superannotate/db/images.py

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424
get_annotation_classes_id_to_name, get_annotation_classes_name_to_id,
2525
search_annotation_classes
2626
)
27-
from .folders import get_folder_metadata
28-
from .project_api import get_project_metadata_bare
27+
from .project_api import get_project_project_folder_metadata
2928

3029
logger = logging.getLogger("superannotate-python-sdk")
3130

@@ -36,8 +35,7 @@ def search_images(
3635
project,
3736
image_name_prefix=None,
3837
annotation_status=None,
39-
return_metadata=False,
40-
project_folder=None
38+
return_metadata=False
4139
):
4240
"""Search images by name_prefix (case-insensitive) and annotation status
4341
@@ -55,17 +53,14 @@ def search_images(
5553
:return: metadata of found images or image names
5654
:rtype: list of dicts or strs
5755
"""
58-
if not isinstance(project, dict):
59-
project = get_project_metadata_bare(project)
56+
project, project_folder = get_project_project_folder_metadata(project)
6057
team_id, project_id = project["team_id"], project["id"]
6158
if annotation_status is not None:
6259
annotation_status = common.annotation_status_str_to_int(
6360
annotation_status
6461
)
6562

6663
if project_folder is not None:
67-
if not isinstance(project_folder, dict):
68-
project_folder = get_folder_metadata(project, project_folder)
6964
project_folder_id = project_folder["id"]
7065
else:
7166
project_folder_id = None
@@ -120,13 +115,7 @@ def process_result(x):
120115
return result_list
121116

122117

123-
@project_metadata
124-
def get_image_metadata(
125-
project,
126-
image_names,
127-
return_dict_on_single_output=True,
128-
project_folder=None
129-
):
118+
def get_image_metadata(project, image_names, return_dict_on_single_output=True):
130119
"""Returns image metadata
131120
132121
:param project: project name or metadata of the project
@@ -137,12 +126,11 @@ def get_image_metadata(
137126
:return: metadata of image
138127
:rtype: dict
139128
"""
129+
project, project_folder = get_project_project_folder_metadata(project)
140130
if isinstance(image_names, str):
141131
image_names = [image_names]
142132

143133
if project_folder is not None:
144-
if not isinstance(project_folder, dict):
145-
project_folder = get_folder_metadata(project, project_folder)
146134
project_folder_id = project_folder["id"]
147135
else:
148136
project_folder_id = None
@@ -546,8 +534,7 @@ def download_image(
546534
0, "Image download variant should be either original or lores"
547535
)
548536

549-
if not isinstance(project, dict):
550-
project = get_project_metadata_bare(project)
537+
project, project_folder = get_project_project_folder_metadata(project)
551538
img = get_image_bytes(project, image_name, variant=variant)
552539
filepath_save = image_name
553540
if variant == "lores":
@@ -665,7 +652,7 @@ def get_image_preannotations(project, image_name):
665652
return _get_image_pre_or_annotations(project, image_name, "pre")
666653

667654

668-
def get_image_annotations(project, image_name, project_folder=None):
655+
def get_image_annotations(project, image_name):
669656
"""Get annotations of the image.
670657
671658
:param project: project name or metadata of the project
@@ -680,20 +667,16 @@ def get_image_annotations(project, image_name, project_folder=None):
680667
"annotation_mask_filename": mask filename on server
681668
:rtype: dict
682669
"""
683-
return _get_image_pre_or_annotations(
684-
project, image_name, "", None, project_folder
685-
)
670+
return _get_image_pre_or_annotations(project, image_name, "", None)
686671

687672

688-
def _get_image_pre_or_annotations(
689-
project, image_name, pre, project_type=None, project_folder=None
690-
):
691-
image = get_image_metadata(project, image_name, True, project_folder)
673+
def _get_image_pre_or_annotations(project, image_name, pre, project_type=None):
674+
image = get_image_metadata(project, image_name, True)
692675
team_id, project_id, image_id, project_folder_id = image["team_id"], image[
693676
"project_id"], image["id"], image['folder_id']
694677
if project_type is None:
695678
if not isinstance(project, dict):
696-
project = get_project_metadata_bare(project)
679+
project, _ = get_project_project_folder_metadata(project)
697680
project_type = project["type"]
698681
params = {
699682
'team_id': team_id,
@@ -790,11 +773,10 @@ def download_image_annotations(project, image_name, local_dir_path):
790773
def _download_image_pre_or_annotations(
791774
project, image_name, local_dir_path, pre
792775
):
793-
if not isinstance(project, dict):
794-
project = get_project_metadata_bare(project)
776+
project, project_folder = get_project_project_folder_metadata(project)
795777

796778
annotation = _get_image_pre_or_annotations(
797-
project, image_name, pre, project["type"]
779+
(project, project_folder), image_name, pre, project["type"]
798780
)
799781

800782
if annotation[f"{pre}annotation_json_filename"] is None:
@@ -869,9 +851,8 @@ def upload_image_annotations(
869851
if verbose:
870852
logger.info("Uploading annotations from %s.", annotation_json)
871853
annotation_json = json.load(open(annotation_json))
872-
if not isinstance(project, dict):
873-
project = get_project_metadata_bare(project)
874-
image = get_image_metadata(project, image_name)
854+
project, project_folder = get_project_project_folder_metadata(project)
855+
image = get_image_metadata((project, project_folder), image_name)
875856
team_id, project_id, image_id, folder_id, image_name = image[
876857
"team_id"], image["project_id"], image["id"], image['folder_id'], image[
877858
'name']

superannotate/db/project_api.py

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .. import common
44
from ..api import API
55
from ..exceptions import (
6-
SABaseException, SAExistingProjectNameException,
6+
SABaseException, SAExistingProjectNameException, SAIncorrectProjectArgument,
77
SANonExistingProjectNameException
88
)
99
from .search_projects import search_projects
@@ -63,3 +63,118 @@ def get_project_metadata_with_users(project_metadata):
6363
contributor["user_role"]
6464
)
6565
return res
66+
67+
68+
def get_folder_metadata(project, folder_name):
69+
if not isinstance(project, dict):
70+
project = get_project_metadata_bare(project)
71+
team_id, project_id = project["team_id"], project["id"]
72+
params = {'team_id': team_id, 'project_id': project_id, 'name': folder_name}
73+
response = _api.send_request(
74+
req_type='GET', path='/folder/getFolderByName', params=params
75+
)
76+
if not response.ok:
77+
raise SABaseException(
78+
response.status_code,
79+
"Couldn't get folder metadata " + response.text
80+
)
81+
res = response.json()
82+
return res
83+
84+
85+
def search_folders(project, folder_name=None, return_metadata=False):
86+
if not isinstance(project, dict):
87+
project = get_project_metadata_bare(project)
88+
team_id, project_id = project["team_id"], project["id"]
89+
result_list = []
90+
params = {
91+
'team_id': team_id,
92+
'project_id': project_id,
93+
'offset': 0,
94+
'name': folder_name,
95+
'is_root': 0
96+
}
97+
total_folders = 0
98+
while True:
99+
response = _api.send_request(
100+
req_type='GET', path='/folders', params=params
101+
)
102+
if not response.ok:
103+
raise SABaseException(
104+
response.status_code, "Couldn't search folders " + response.text
105+
)
106+
response = response.json()
107+
results_folders = response["data"]
108+
for r in results_folders:
109+
if return_metadata:
110+
result_list.append(r)
111+
else:
112+
result_list.append(r["name"])
113+
114+
total_folders += len(results_folders)
115+
if response["count"] <= total_folders:
116+
break
117+
118+
params["offset"] = total_folders
119+
120+
return result_list
121+
122+
123+
def create_folder(project, folder_name):
124+
"""Create a new folder in the project.
125+
126+
:param project: project name or metadata of the project to be deleted
127+
:type project: str or dict
128+
:param folder_name: the new folder's name
129+
:type folder_name: str
130+
131+
:return: dict object metadata the new folder
132+
:rtype: dict
133+
"""
134+
if not isinstance(project, dict):
135+
project = get_project_metadata_bare(project)
136+
params = {"team_id": project["team_id"], "project_id": project["id"]}
137+
data = {"name": folder_name}
138+
response = _api.send_request(
139+
req_type='POST', path='/folder', params=params, json_req=data
140+
)
141+
if not response.ok:
142+
raise SABaseException(
143+
response.status_code, "Couldn't create project " + response.text
144+
)
145+
res = response.json()
146+
if res["name"] != folder_name:
147+
logger.warning(
148+
"Created folder has name %s, since folder with name %s already existed.",
149+
res["name"], folder_name
150+
)
151+
return res
152+
153+
154+
def get_project_project_folder_metadata(project):
155+
if isinstance(project, dict):
156+
project = project
157+
folder = None
158+
elif isinstance(project, tuple):
159+
if len(project) != 2:
160+
raise SAIncorrectProjectArgument(project)
161+
project, folder = project
162+
if not isinstance(project, dict):
163+
raise SAIncorrectProjectArgument(project)
164+
if folder is not None and not isinstance(project, dict):
165+
raise SAIncorrectProjectArgument(project)
166+
elif isinstance(project, str):
167+
parts = project.split('/')
168+
if len(parts) == 1:
169+
project_name = parts[0]
170+
project = get_project_metadata_bare(project_name)
171+
folder = None
172+
elif len(parts) == 2:
173+
project_name, folder_name = parts
174+
project = get_project_metadata_bare(project_name)
175+
folder = get_folder_metadata(project, folder_name)
176+
else:
177+
raise SAIncorrectProjectArgument(project)
178+
else:
179+
raise SAIncorrectProjectArgument(project)
180+
return project, folder

0 commit comments

Comments
 (0)