Skip to content

Commit 2d60c69

Browse files
committed
Create folders
1 parent 21ba1ee commit 2d60c69

File tree

5 files changed

+123
-115
lines changed

5 files changed

+123
-115
lines changed

superannotate/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ 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
4546
from .db.images import (
4647
add_annotation_bbox_to_image, add_annotation_comment_to_image,
4748
add_annotation_cuboid_to_image, add_annotation_ellipse_to_image,
4849
add_annotation_point_to_image, add_annotation_polygon_to_image,
4950
add_annotation_polyline_to_image, add_annotation_template_to_image,
50-
create_folder, create_fuse_image, delete_image, download_image,
51-
download_image_annotations, download_image_preannotations,
52-
get_folder_metadata, get_image_annotations, get_image_bytes,
53-
get_image_metadata, get_image_preannotations, search_folders, search_images,
51+
create_fuse_image, delete_image, download_image, download_image_annotations,
52+
download_image_preannotations, get_image_annotations, get_image_bytes,
53+
get_image_metadata, get_image_preannotations, search_images,
5454
set_image_annotation_status, upload_image_annotations
5555
)
5656
from .db.project_images import (

superannotate/db/folders.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from ..api import API
2+
from ..exceptions import SABaseException
3+
from .project_api import get_project_metadata_bare
4+
5+
_api = API.get_instance()
6+
7+
8+
def get_folder_metadata(project, folder_name):
9+
if not isinstance(project, dict):
10+
project = get_project_metadata_bare(project)
11+
team_id, project_id = project["team_id"], project["id"]
12+
params = {'team_id': team_id, 'project_id': project_id, 'name': folder_name}
13+
response = _api.send_request(
14+
req_type='GET', path='/folder/getFolderByName', params=params
15+
)
16+
if not response.ok:
17+
raise SABaseException(
18+
response.status_code,
19+
"Couldn't get folder metadata " + response.text
20+
)
21+
res = response.json()
22+
return res
23+
24+
25+
def search_folders(project, folder_name=None, return_metadata=False):
26+
if not isinstance(project, dict):
27+
project = get_project_metadata_bare(project)
28+
team_id, project_id = project["team_id"], project["id"]
29+
result_list = []
30+
params = {'team_id': team_id, 'project_id': project_id, 'offset': 0}
31+
total_got = 0
32+
total_folders = 0
33+
while True:
34+
response = _api.send_request(
35+
req_type='GET', path='/images', params=params
36+
)
37+
if not response.ok:
38+
raise SABaseException(
39+
response.status_code, "Couldn't search images " + response.text
40+
)
41+
response = response.json()
42+
images = response["images"]
43+
folders = response["folders"]
44+
45+
results_folders = folders["data"]
46+
for r in results_folders:
47+
if folder_name is not None and r["name"] != folder_name:
48+
continue
49+
if return_metadata:
50+
result_list.append(r)
51+
else:
52+
result_list.append(r["name"])
53+
54+
total_folders += len(results_folders)
55+
if folders["count"] <= total_folders:
56+
break
57+
58+
total_got += len(images["data"]) + len(results_folders)
59+
params["offset"] = total_got
60+
61+
return result_list
62+
63+
64+
def create_folder(project, folder_name):
65+
"""Create a new folder in the project.
66+
67+
:param project: project name or metadata of the project to be deleted
68+
:type project: str or dict
69+
:param folder_name: the new folder's name
70+
:type folder_name: str
71+
72+
:return: dict object metadata the new folder
73+
:rtype: dict
74+
"""
75+
if not isinstance(project, dict):
76+
project = get_project_metadata_bare(project)
77+
params = {"team_id": project["team_id"], "project_id": project["id"]}
78+
data = {"name": folder_name}
79+
response = _api.send_request(
80+
req_type='POST', path='/folder', params=params, json_req=data
81+
)
82+
if not response.ok:
83+
raise SABaseException(
84+
response.status_code, "Couldn't create project " + response.text
85+
)
86+
res = response.json()
87+
return res

superannotate/db/images.py

Lines changed: 1 addition & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -24,58 +24,14 @@
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
2728
from .project_api import get_project_metadata_bare
2829

2930
logger = logging.getLogger("superannotate-python-sdk")
3031

3132
_api = API.get_instance()
3233

3334

34-
def _get_project_root_folder_id(project):
35-
"""Get root folder ID
36-
Returns
37-
-------
38-
int
39-
Root folder ID
40-
"""
41-
params = {'team_id': project['team_id']}
42-
response = _api.send_request(
43-
req_type='GET', path=f'/project/{project["id"]}', params=params
44-
)
45-
if not response.ok:
46-
raise SABaseException(response.status_code, response.text)
47-
48-
response = response.json()
49-
50-
return response['folder_id']
51-
52-
53-
def create_folder(project, folder_name):
54-
"""Create a new folder in the project.
55-
56-
:param project: project name or metadata of the project to be deleted
57-
:type project: str or dict
58-
:param folder_name: the new folder's name
59-
:type folder_name: str
60-
61-
:return: dict object metadata the new folder
62-
:rtype: dict
63-
"""
64-
if not isinstance(project, dict):
65-
project = get_project_metadata_bare(project)
66-
params = {"team_id": project["team_id"], "project_id": project["id"]}
67-
data = {"name": folder_name}
68-
response = _api.send_request(
69-
req_type='POST', path='/folder', params=params, json_req=data
70-
)
71-
if not response.ok:
72-
raise SABaseException(
73-
response.status_code, "Couldn't create project " + response.text
74-
)
75-
res = response.json()
76-
return res
77-
78-
7935
def search_images(
8036
project,
8137
image_name_prefix=None,
@@ -163,62 +119,6 @@ def process_result(x):
163119
return result_list
164120

165121

166-
def get_folder_metadata(project, folder_name):
167-
if not isinstance(project, dict):
168-
project = get_project_metadata_bare(project)
169-
team_id, project_id = project["team_id"], project["id"]
170-
params = {'team_id': team_id, 'project_id': project_id, 'name': folder_name}
171-
response = _api.send_request(
172-
req_type='GET', path='/folder/getFolderByName', params=params
173-
)
174-
if not response.ok:
175-
raise SABaseException(
176-
response.status_code,
177-
"Couldn't get folder metadata " + response.text
178-
)
179-
res = response.json()
180-
return res
181-
182-
183-
def search_folders(project, folder_name=None, return_metadata=False):
184-
if not isinstance(project, dict):
185-
project = get_project_metadata_bare(project)
186-
team_id, project_id = project["team_id"], project["id"]
187-
result_list = []
188-
params = {'team_id': team_id, 'project_id': project_id, 'offset': 0}
189-
total_got = 0
190-
total_folders = 0
191-
while True:
192-
response = _api.send_request(
193-
req_type='GET', path='/images', params=params
194-
)
195-
if not response.ok:
196-
raise SABaseException(
197-
response.status_code, "Couldn't search images " + response.text
198-
)
199-
response = response.json()
200-
images = response["images"]
201-
folders = response["folders"]
202-
203-
results_folders = folders["data"]
204-
for r in results_folders:
205-
if folder_name is not None and r["name"] != folder_name:
206-
continue
207-
if return_metadata:
208-
result_list.append(r)
209-
else:
210-
result_list.append(r["name"])
211-
212-
total_folders += len(results_folders)
213-
if folders["count"] <= total_folders:
214-
break
215-
216-
total_got += len(images["data"]) + len(results_folders)
217-
params["offset"] = total_got
218-
219-
return result_list
220-
221-
222122
@project_metadata
223123
def get_image_metadata(project, image_names, return_dict_on_single_output=True):
224124
"""Returns image metadata

superannotate/db/projects.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
fill_class_and_attribute_ids, get_annotation_classes_name_to_id,
3535
search_annotation_classes
3636
)
37+
from .folders import get_folder_metadata
3738
from .images import get_image_metadata, search_images
3839
from .project_api import get_project_metadata_bare
3940
from .users import get_team_contributor_metadata
@@ -438,7 +439,8 @@ def upload_images_from_folder_to_project(
438439
from_s3_bucket=None,
439440
exclude_file_patterns=None,
440441
recursive_subfolders=False,
441-
image_quality_in_editor=None
442+
image_quality_in_editor=None,
443+
folder=None
442444
):
443445
"""Uploads all images with given extensions from folder_path to the project.
444446
Sets status of all the uploaded images to set_status if it is not None.
@@ -536,7 +538,7 @@ def upload_images_from_folder_to_project(
536538

537539
return upload_images_to_project(
538540
project, filtered_paths, annotation_status, from_s3_bucket,
539-
image_quality_in_editor
541+
image_quality_in_editor, folder
540542
)
541543

542544

@@ -640,7 +642,7 @@ def get_image_array_to_upload(
640642
def __upload_images_to_aws_thread(
641643
res, img_paths, project, annotation_status, prefix, thread_id, chunksize,
642644
couldnt_upload, uploaded, tried_upload, image_quality_in_editor,
643-
from_s3_bucket
645+
from_s3_bucket, folder_id
644646
):
645647
len_img_paths = len(img_paths)
646648
start_index = thread_id * chunksize
@@ -696,7 +698,8 @@ def __upload_images_to_aws_thread(
696698
try:
697699
__create_image(
698700
uploaded_imgs_info[0], uploaded_imgs_info[1], project,
699-
annotation_status, prefix, uploaded_imgs_info[2]
701+
annotation_status, prefix, uploaded_imgs_info[2],
702+
folder_id
700703
)
701704
except SABaseException as e:
702705
couldnt_upload[thread_id] += uploaded_imgs
@@ -708,7 +711,7 @@ def __upload_images_to_aws_thread(
708711
try:
709712
__create_image(
710713
uploaded_imgs_info[0], uploaded_imgs_info[1], project,
711-
annotation_status, prefix, uploaded_imgs_info[2]
714+
annotation_status, prefix, uploaded_imgs_info[2], folder_id
712715
)
713716
except SABaseException as e:
714717
couldnt_upload[thread_id] += uploaded_imgs
@@ -718,7 +721,8 @@ def __upload_images_to_aws_thread(
718721

719722

720723
def __create_image(
721-
img_names, img_paths, project, annotation_status, remote_dir, sizes
724+
img_names, img_paths, project, annotation_status, remote_dir, sizes,
725+
folder_id
722726
):
723727
if len(img_paths) == 0:
724728
return
@@ -733,6 +737,8 @@ def __create_image(
733737
"annotation_status": annotation_status,
734738
"meta": {}
735739
}
740+
if folder_id is not None:
741+
data["folder_id"] = folder_id
736742
for img_name, img_path, size in zip(img_names, img_paths, sizes):
737743
img_name_uuid = Path(img_path).name
738744
remote_path = remote_dir + f"{img_name_uuid}"
@@ -747,6 +753,7 @@ def __create_image(
747753
response = _api.send_request(
748754
req_type='POST', path='/image/ext-create', json_req=data
749755
)
756+
print(data)
750757
if not response.ok:
751758
raise SABaseException(
752759
response.status_code, "Couldn't ext-create image " + response.text
@@ -758,7 +765,8 @@ def upload_images_to_project(
758765
img_paths,
759766
annotation_status="NotStarted",
760767
from_s3_bucket=None,
761-
image_quality_in_editor=None
768+
image_quality_in_editor=None,
769+
folder=None
762770
):
763771
"""Uploads all images given in list of path objects in img_paths to the project.
764772
Sets status of all the uploaded images to set_status if it is not None.
@@ -794,7 +802,7 @@ def upload_images_to_project(
794802
project
795803
)
796804
team_id, project_id = project["team_id"], project["id"]
797-
existing_images = search_images(project)
805+
existing_images = search_images(project, folder=folder)
798806
duplicate_images = []
799807
for existing_image in existing_images:
800808
i = -1
@@ -831,6 +839,13 @@ def upload_images_to_project(
831839
raise SABaseException(
832840
response.status_code, "Couldn't get upload token " + response.text
833841
)
842+
if folder is not None:
843+
if not isinstance(folder, dict):
844+
folder_id = get_folder_metadata(project, folder)
845+
else:
846+
folder_id = folder["id"]
847+
else:
848+
folder_id = None
834849
res = response.json()
835850
prefix = res['filePath']
836851
tqdm_thread = threading.Thread(
@@ -847,7 +862,7 @@ def upload_images_to_project(
847862
args=(
848863
res, img_paths, project, annotation_status, prefix, thread_id,
849864
chunksize, couldnt_upload, uploaded, tried_upload,
850-
image_quality_in_editor, from_s3_bucket
865+
image_quality_in_editor, from_s3_bucket, folder_id
851866
),
852867
daemon=True
853868
)

tests/test_folders.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,10 @@ def test_basic_folders(tmpdir):
4949

5050
with pytest.raises(SABaseException) as e:
5151
folder = sa.get_folder_metadata(project, "folder2")
52-
assert 'Folder not found' in str(e)
52+
assert 'Folder not found' in str(e)
53+
54+
sa.upload_images_from_folder_to_project(
55+
project, FROM_FOLDER, annotation_status="InProgress", folder="folder1"
56+
)
57+
images = sa.search_images(project, "example_image_1", folder="folder1")
58+
assert len(images) == 1

0 commit comments

Comments
 (0)