Skip to content

Commit 664641e

Browse files
committed
Add limitations
1 parent d32cb8d commit 664641e

File tree

4 files changed

+102
-86
lines changed

4 files changed

+102
-86
lines changed

superannotate/db/images.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -231,30 +231,32 @@ def get_image_metadata(project, image_names, return_dict_on_single_output=True):
231231
else:
232232
project_folder_id = None
233233

234+
chunk_size = 500
235+
chunks = [image_names[i:i + chunk_size] for i in range(0, len(image_names), chunk_size)]
236+
234237
json_req = {
235238
'project_id': project['id'],
236239
'team_id': _api.team_id,
237-
'names': image_names,
238240
}
241+
239242
if project_folder_id is not None:
240243
json_req["folder_id"] = project_folder_id
241-
response = _api.send_request(
242-
req_type='POST',
243-
path='/images/getBulk',
244-
json_req=json_req,
245-
)
246-
if not response.ok:
247-
raise SABaseException(
248-
response.status_code,
249-
"Couldn't get image metadata. " + response.text
250-
)
251244

252-
metadata_raw = response.json()
245+
metadata_raw = []
246+
for chunk in chunks:
247+
json_req['names'] = chunk
248+
response = _api.send_request(
249+
req_type='POST',
250+
path='/images/getBulk',
251+
json_req=json_req,
252+
)
253+
if not response.ok:
254+
raise SABaseException(response.status_code,"Couldn't get image metadata. " + response.text)
255+
metadata_raw += response.json()
256+
253257
metadata_without_deleted = []
254-
for im_metadata in metadata_raw:
255-
if 'delete' in im_metadata and im_metadata['delete'] == 1:
256-
continue
257-
metadata_without_deleted.append(im_metadata)
258+
metadata_without_deleted = [ i for i in metadata_raw if i['delete'] != 1 ]
259+
258260
if len(metadata_without_deleted) == 0:
259261
raise SABaseException(
260262
0,

superannotate/db/project_images.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
__create_image, get_image_array_to_upload,
2020
get_project_default_image_quality_in_editor, upload_image_array_to_s3
2121
)
22+
from .utils import _get_upload_auth_token
2223

2324
logger = logging.getLogger("superannotate-python-sdk")
2425
_api = API.get_instance()
@@ -50,7 +51,7 @@ def upload_image_to_project(
5051
Can be either "compressed" or "original". If None then the default value in project settings will be used.
5152
:type image_quality_in_editor: str
5253
"""
53-
project, project_folder = get_project_and_folder_metadata(project)
54+
project, folder = get_project_and_folder_metadata(project)
5455
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
5556
if upload_state == "External":
5657
raise SABaseException(
@@ -92,21 +93,18 @@ def upload_image_to_project(
9293
0, "Image name img_name should be set if img is not Pathlike"
9394
)
9495

96+
if folder:
97+
folder_id = folder["id"]
98+
else:
99+
folder_id = get_project_root_folder_id(project)
100+
95101
team_id, project_id = project["team_id"], project["id"]
96102
params = {
97103
'team_id': team_id,
104+
'folder_id' : folder_id
98105
}
99-
response = _api.send_request(
100-
req_type='GET',
101-
path=f'/project/{project_id}/sdkImageUploadToken',
102-
params=params
103-
)
104-
if not response.ok:
105-
raise SABaseException(
106-
response.status_code, "Couldn't get upload token " + response.text
107-
)
108-
res = response.json()
109-
prefix = res['filePath']
106+
res = _get_upload_auth_token(params=params,project_id=project_id)
107+
prefix = res['filePath']
110108
s3_session = boto3.Session(
111109
aws_access_key_id=res['accessKeyId'],
112110
aws_secret_access_key=res['secretAccessKey'],
@@ -122,16 +120,12 @@ def upload_image_to_project(
122120
except Exception as e:
123121
raise SABaseException(0, "Couldn't upload to data server. " + str(e))
124122

125-
if project_folder is not None:
126-
project_folder_id = project_folder["id"]
127-
else:
128-
project_folder_id = None
129123
__create_image(
130124
[img_name], [key],
131125
project,
132126
annotation_status,
133127
prefix, [images_info_and_array[2]],
134-
project_folder_id,
128+
folder_id,
135129
upload_state="Basic"
136130
)
137131

@@ -171,7 +165,7 @@ def _copy_images(
171165
destination_folder_id = get_project_root_folder_id(destination_project)
172166
json_req["destination_folder_id"] = destination_folder_id
173167
res = {}
174-
res['skipped'] = 0
168+
res['skipped'] = []
175169
for start_index in range(0, len(image_names), NUM_TO_SEND):
176170
json_req["image_names"] = image_names[start_index:start_index +
177171
NUM_TO_SEND]
@@ -239,7 +233,7 @@ def copy_images(
239233
source_project["name"] + "" if source_project_folder is None else "/" +
240234
source_project_folder["name"], destination_project["name"] +
241235
"" if destination_project_folder is None else "/" +
242-
destination_project_folder["name"], res["skipped"]
236+
destination_project_folder["name"], len(res["skipped"])
243237
)
244238
return res["skipped"]
245239

superannotate/db/projects.py

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@
3434
fill_class_and_attribute_ids, get_annotation_classes_name_to_id,
3535
search_annotation_classes
3636
)
37-
from .images import get_image_metadata, search_images, search_images_all_folders
37+
from .images import get_image_metadata, search_images, search_images_all_folders, get_project_root_folder_id
3838
from .project_api import (
3939
get_project_and_folder_metadata, get_project_metadata_bare,
4040
get_project_metadata_with_users
4141
)
4242
from .users import get_team_contributor_metadata
43+
from .utils import _get_upload_auth_token
4344

4445
logger = logging.getLogger("superannotate-python-sdk")
4546

@@ -827,9 +828,9 @@ def upload_images_to_project(
827828
:return: uploaded, could-not-upload, existing-images filepaths
828829
:rtype: tuple (3 members) of list of strs
829830
"""
830-
project, project_folder = get_project_and_folder_metadata(project)
831-
project_folder_name = project["name"] + (
832-
f'/{project_folder["name"]}' if project_folder else ""
831+
project, folder = get_project_and_folder_metadata(project)
832+
folder_name = project["name"] + (
833+
f'/{folder["name"]}' if folder else ""
833834
)
834835
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
835836
if upload_state == "External":
@@ -847,7 +848,7 @@ def upload_images_to_project(
847848
project
848849
)
849850
team_id, project_id = project["team_id"], project["id"]
850-
existing_images = search_images((project, project_folder))
851+
existing_images = search_images((project, folder))
851852
duplicate_images = []
852853
for existing_image in existing_images:
853854
i = -1
@@ -865,31 +866,31 @@ def upload_images_to_project(
865866
)
866867
len_img_paths = len(img_paths)
867868
logger.info(
868-
"Uploading %s images to project %s.", len_img_paths, project_folder_name
869+
"Uploading %s images to project %s.", len_img_paths, folder_name
869870
)
870871
if len_img_paths == 0:
871872
return ([], [], duplicate_images)
872-
params = {'team_id': team_id}
873+
874+
875+
if folder:
876+
folder_id = folder["id"]
877+
else:
878+
folder_id = get_project_root_folder_id(project)
879+
880+
params = {'team_id': team_id , 'folder_id' : folder_id }
873881
uploaded = [[] for _ in range(_NUM_THREADS)]
874882
tried_upload = [[] for _ in range(_NUM_THREADS)]
875883
couldnt_upload = [[] for _ in range(_NUM_THREADS)]
876884
finish_event = threading.Event()
877-
chunksize = int(math.ceil(len(img_paths) / _NUM_THREADS))
878-
response = _api.send_request(
879-
req_type='GET',
880-
path=f'/project/{project_id}/sdkImageUploadToken',
881-
params=params
882-
)
883-
if not response.ok:
884-
raise SABaseException(
885-
response.status_code, "Couldn't get upload token " + response.text
886-
)
887-
if project_folder is not None:
888-
project_folder_id = project_folder["id"]
889-
else:
890-
project_folder_id = None
891-
res = response.json()
885+
886+
res = _get_upload_auth_token(params=params,project_id=project_id)
887+
892888
prefix = res['filePath']
889+
limit = res['availableImageCount']
890+
images_to_upload = img_paths[:limit]
891+
images_to_skip = img_paths[limit:]
892+
chunksize = int(math.ceil(len(images_to_upload) / _NUM_THREADS))
893+
893894
tqdm_thread = threading.Thread(
894895
target=__tqdm_thread_image_upload,
895896
args=(len_img_paths, tried_upload, finish_event),
@@ -902,9 +903,9 @@ def upload_images_to_project(
902903
t = threading.Thread(
903904
target=__upload_images_to_aws_thread,
904905
args=(
905-
res, img_paths, project, annotation_status, prefix, thread_id,
906+
res, images_to_upload, project, annotation_status, prefix, thread_id,
906907
chunksize, couldnt_upload, uploaded, tried_upload,
907-
image_quality_in_editor, from_s3_bucket, project_folder_id
908+
image_quality_in_editor, from_s3_bucket, folder_id
908909
),
909910
daemon=True
910911
)
@@ -923,6 +924,7 @@ def upload_images_to_project(
923924
for f in upload_thread:
924925
list_of_uploaded.append(str(f))
925926

927+
list_of_not_uploaded += images_to_skip
926928
return (list_of_uploaded, list_of_not_uploaded, duplicate_images)
927929

928930

@@ -960,9 +962,9 @@ def attach_image_urls_to_project(
960962
:rtype: tuple
961963
"""
962964

963-
project, project_folder = get_project_and_folder_metadata(project)
964-
project_folder_name = project["name"] + (
965-
f'/{project_folder["name"]}' if project_folder else ""
965+
project, folder = get_project_and_folder_metadata(project)
966+
folder_name = project["name"] + (
967+
f'/{folder["name"]}' if folder else ""
966968
)
967969
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
968970
if upload_state == "Basic":
@@ -978,7 +980,7 @@ def attach_image_urls_to_project(
978980
duplicate_idx_csv = existing_names.duplicated(subset="name", keep="first")
979981
duplicate_images = existing_names[duplicate_idx_csv]["name"].tolist()
980982
existing_names = existing_names[~duplicate_idx_csv]
981-
existing_images = search_images((project, project_folder))
983+
existing_images = search_images((project, folder))
982984
duplicate_idx = []
983985
for ind, _ in image_data[image_data["name"].isnull()].iterrows():
984986
while True:
@@ -1000,37 +1002,35 @@ def attach_image_urls_to_project(
10001002
)
10011003
image_data = pd.DataFrame(image_data, columns=["name", "url"])
10021004
img_names_urls = image_data.values.tolist()
1003-
len_img_names_urls = len(img_names_urls)
10041005
logger.info(
1005-
"Uploading %s images to project %s.", len_img_names_urls,
1006-
project_folder_name
1006+
"Uploading %s images to project %s.", len(img_names_urls),
1007+
folder_name
10071008
)
1008-
if len_img_names_urls == 0:
1009+
if len(img_names_urls) == 0:
10091010
return ([], [], duplicate_images)
1010-
params = {'team_id': team_id}
1011+
1012+
if folder:
1013+
folder_id = folder["id"]
1014+
else:
1015+
folder_id = get_project_root_folder_id(project)
1016+
1017+
params = {'team_id': team_id , 'folder_id' : folder_id }
10111018
uploaded = [[] for _ in range(_NUM_THREADS)]
10121019
tried_upload = [[] for _ in range(_NUM_THREADS)]
10131020
couldnt_upload = [[] for _ in range(_NUM_THREADS)]
10141021
finish_event = threading.Event()
1015-
chunksize = int(math.ceil(len_img_names_urls / _NUM_THREADS))
1016-
response = _api.send_request(
1017-
req_type='GET',
1018-
path=f'/project/{project_id}/sdkImageUploadToken',
1019-
params=params
1020-
)
1021-
if not response.ok:
1022-
raise SABaseException(
1023-
response.status_code, "Couldn't get upload token " + response.text
1024-
)
1025-
if project_folder is not None:
1026-
project_folder_id = project_folder["id"]
1027-
else:
1028-
project_folder_id = None
1029-
res = response.json()
1022+
1023+
res = _get_upload_auth_token(params=params,project_id=project_id)
1024+
10301025
prefix = res['filePath']
1026+
limit = res['availableImageCount']
1027+
images_to_upload = img_names_urls[:limit]
1028+
images_to_skip = img_names_urls[limit:]
1029+
chunksize = int(math.ceil(len(images_to_upload) / _NUM_THREADS))
1030+
10311031
tqdm_thread = threading.Thread(
10321032
target=__tqdm_thread_image_upload,
1033-
args=(len_img_names_urls, tried_upload, finish_event),
1033+
args=(len(images_to_upload), tried_upload, finish_event),
10341034
daemon=True
10351035
)
10361036
tqdm_thread.start()
@@ -1039,9 +1039,9 @@ def attach_image_urls_to_project(
10391039
t = threading.Thread(
10401040
target=__attach_image_urls_to_project_thread,
10411041
args=(
1042-
res, img_names_urls, project, annotation_status, prefix,
1042+
res, images_to_upload, project, annotation_status, prefix,
10431043
thread_id, chunksize, couldnt_upload, uploaded, tried_upload,
1044-
project_folder_id
1044+
folder_id
10451045
),
10461046
daemon=True
10471047
)
@@ -1060,6 +1060,7 @@ def attach_image_urls_to_project(
10601060
for f in upload_thread:
10611061
list_of_uploaded.append(str(f))
10621062

1063+
list_of_not_uploaded += [i[0] for i in images_to_skip ]
10631064
return (list_of_uploaded, list_of_not_uploaded, duplicate_images)
10641065

10651066

superannotate/db/utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from ..api import API
2+
from ..exceptions import SABaseException, SAImageSizeTooLarge
3+
_api = API.get_instance()
4+
5+
6+
def _get_upload_auth_token(params,project_id):
7+
response = _api.send_request(
8+
req_type='GET',
9+
path=f'/project/{project_id}/sdkImageUploadToken',
10+
params=params
11+
)
12+
if not response.ok:
13+
raise SABaseException(
14+
response.status_code, "Couldn't get upload token " + response.text
15+
)
16+
17+
res = response.json()
18+
return res
19+

0 commit comments

Comments
 (0)