Skip to content

Commit 75221a8

Browse files
committed
Merge develop
2 parents 664641e + 32fba21 commit 75221a8

15 files changed

+83
-72
lines changed

.pylintrc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[BASIC]
2-
good-names=df,f,e,i,j,k,im,pt,pr
2+
good-names=df,f,e,i,j,k,im,pt,pr,m
33

44
bad-functions=
55
apply,
@@ -23,7 +23,8 @@ disable=
2323
no-else-return,
2424
too-many-lines,
2525
too-many-arguments,
26-
too-many-instance-attributes
26+
too-many-instance-attributes,
27+
broad-except
2728

2829
[SPELLING]
2930

docs/source/cli.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@ to look for. If the argument is not given then value *jpg,jpeg,png,tif,tiff,webp
7979

8080
----------
8181

82+
.. _ref_attach_image_urls:
83+
84+
Attaching image URLs
85+
~~~~~~~~~~~~~~~~~~~~
86+
87+
To attach image URLs to project use:
88+
89+
.. code-block:: bash
90+
91+
superannotatecli attach-image-urls --project <project_name/folder_name> --attachments <csv_path> [--annotation_status <annotation_status>]
92+
93+
----------
94+
8295
.. _ref_upload_videos:
8396

8497
Uploading videos

superannotate/db/debug_interview.py

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

superannotate/db/exports.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import zipfile
77
from datetime import datetime
88
from pathlib import Path
9+
import shutil
910

1011
import boto3
1112
import requests
@@ -128,6 +129,7 @@ def prepare_export(
128129
logger.info(
129130
"Include fuse functionality is not supported for projects containing images attached with URLs"
130131
)
132+
include_fuse = False
131133
team_id, project_id = project["team_id"], project["id"]
132134
if annotation_statuses is None:
133135
annotation_statuses = [2, 3, 4, 5]
@@ -208,6 +210,15 @@ def __upload_files_to_aws_thread(
208210
already_uploaded[i] = True
209211

210212

213+
def _download_file(url, local_filename):
214+
with requests.get(url, stream=True) as r:
215+
r.raise_for_status()
216+
with open(local_filename, 'wb') as f:
217+
for chunk in r.iter_content(chunk_size=8192):
218+
f.write(chunk)
219+
return local_filename
220+
221+
211222
def download_export(
212223
project, export, folder_path, extract_zip_contents=True, to_s3_bucket=None
213224
):
@@ -242,25 +253,26 @@ def download_export(
242253
break
243254

244255
filename = Path(res['path']).name
245-
r = requests.get(res['download'], allow_redirects=True)
246-
if to_s3_bucket is None:
247-
filepath = Path(folder_path) / filename
248-
open(filepath, 'wb').write(r.content)
249-
if extract_zip_contents:
250-
with zipfile.ZipFile(filepath, 'r') as f:
251-
f.extractall(folder_path)
252-
Path.unlink(filepath)
253-
logger.info("Extracted %s to folder %s", filepath, folder_path)
254-
else:
255-
logger.info("Downloaded export ID %s to %s", res['id'], filepath)
256-
else:
257-
with tempfile.TemporaryDirectory() as tmpdirname:
258-
filepath = Path(tmpdirname) / filename
259-
open(filepath, 'wb').write(r.content)
256+
with tempfile.TemporaryDirectory() as tmpdirname:
257+
temp_filepath = Path(tmpdirname) / filename
258+
_download_file(res['download'], temp_filepath)
259+
if to_s3_bucket is None:
260+
filepath = Path(folder_path) / filename
261+
shutil.copyfile(temp_filepath, filepath)
260262
if extract_zip_contents:
261263
with zipfile.ZipFile(filepath, 'r') as f:
262-
f.extractall(tmpdirname)
264+
f.extractall(folder_path)
263265
Path.unlink(filepath)
266+
logger.info("Extracted %s to folder %s", filepath, folder_path)
267+
else:
268+
logger.info(
269+
"Downloaded export ID %s to %s", res['id'], filepath
270+
)
271+
else:
272+
if extract_zip_contents:
273+
with zipfile.ZipFile(temp_filepath, 'r') as f:
274+
f.extractall(tmpdirname)
275+
Path.unlink(temp_filepath)
264276
files_to_upload = []
265277
for file in Path(tmpdirname).rglob("*.*"):
266278
if not file.is_file():
@@ -295,4 +307,4 @@ def download_export(
295307
t.join()
296308
finish_event.set()
297309
tqdm_thread.join()
298-
logger.info("Exported to AWS %s/%s", to_s3_bucket, folder_path)
310+
logger.info("Exported to AWS %s/%s", to_s3_bucket, folder_path)

superannotate/db/images.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ def get_image_bytes(project, image_name, variant='original'):
722722
raise SABaseException(
723723
0, "Image download variant should be either original or lores"
724724
)
725-
image = get_image_metadata(project, image_name)
725+
image = get_image_metadata((project, project_folder), image_name)
726726
team_id, project_id, image_id, folder_id = image["team_id"], image[
727727
"project_id"], image["id"], image['folder_id']
728728
params = {
@@ -1223,7 +1223,7 @@ def set_images_annotation_statuses(project, image_names, annotation_status):
12231223
data["image_names"] = image_names[start_index:start_index + NUM_TO_SEND]
12241224
response = _api.send_request(
12251225
req_type='PUT',
1226-
path=f'/image/updateAnnotationStatusBulk',
1226+
path='/image/updateAnnotationStatusBulk',
12271227
params=params,
12281228
json_req=data
12291229
)

superannotate/db/project_api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ def get_project_and_folder_metadata(project):
105105
:rtype: dict
106106
"""
107107
if isinstance(project, dict):
108-
project = project
109108
folder = None
110109
elif isinstance(project, tuple):
111110
if len(project) != 2:

superannotate/db/project_images.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def upload_image_to_project(
118118
)
119119
key = upload_image_array_to_s3(bucket, *images_info_and_array, prefix)
120120
except Exception as e:
121-
raise SABaseException(0, "Couldn't upload to data server. " + str(e))
121+
raise SABaseException(0, "Couldn't upload to data server.") from e
122122

123123
__create_image(
124124
[img_name], [key],
@@ -375,18 +375,15 @@ def copy_image(
375375
except SABaseException:
376376
break
377377
else:
378-
found_copied = False
379378
for m in p.finditer(new_name):
380379
if m.start() + len(m.group()
381380
) + len(extension) - 1 == len(new_name):
382381
num = int(m.group()[2:-2])
383-
found_copied = True
382+
new_name = new_name[:m.start() +
383+
2] + str(num + 1) + ")" + extension
384384
break
385-
if not found_copied:
386-
new_name = Path(new_name).stem + "_(1)" + extension
387385
else:
388-
new_name = new_name[:m.start() +
389-
2] + str(num + 1) + ")" + extension
386+
new_name = Path(new_name).stem + "_(1)" + extension
390387

391388
upload_image_to_project(
392389
(destination_project, destination_project_folder), img_b, new_name

superannotate/db/projects.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ def upload_video_to_project(
266266
if "ffprobe" in str(e):
267267
warning_str = "This could be because ffmpeg package is not installed. To install it, run: sudo apt install ffmpeg"
268268
logger.warning(
269-
"Couldn't read video metadata to determine rotation. " + warning_str
269+
"Couldn't read video metadata to determine rotation. %s",
270+
warning_str
270271
)
271272

272273
video = cv2.VideoCapture(str(video_path), cv2.CAP_FFMPEG)
@@ -340,7 +341,7 @@ def upload_video_to_project(
340341
)
341342

342343
filenames = upload_images_from_folder_to_project(
343-
project,
344+
(project, project_folder),
344345
tempdir.name,
345346
extensions=["jpg"],
346347
annotation_status=annotation_status,
@@ -767,9 +768,6 @@ def __create_image(
767768
"team_id": str(team_id),
768769
"images": [],
769770
"annotation_status": annotation_status,
770-
"team_id": str(team_id),
771-
"images": [],
772-
"annotation_status": annotation_status,
773771
"meta": {},
774772
"upload_state": upload_state_code
775773
}
@@ -1086,7 +1084,7 @@ def __attach_image_urls_to_project_thread(
10861084
for i in range(start_index, end_index):
10871085
if i >= len_img_paths:
10881086
break
1089-
name, url = img_names_urls[i]
1087+
name, _ = img_names_urls[i]
10901088
tried_upload[thread_id].append(name)
10911089
img_name_hash = str(uuid.uuid4()) + Path(name).suffix
10921090
key = prefix + img_name_hash
@@ -1190,7 +1188,7 @@ def upload_images_from_public_urls_to_project(
11901188
),
11911189
daemon=True
11921190
)
1193-
logger.info('Downloading %s images' % len(img_urls))
1191+
logger.info('Downloading %s images', len(img_urls))
11941192
tqdm_thread.start()
11951193
with tempfile.TemporaryDirectory() as save_dir_name:
11961194
save_dir = Path(save_dir_name)
@@ -1227,7 +1225,7 @@ def upload_images_from_public_urls_to_project(
12271225
finish_event.set()
12281226
tqdm_thread.join()
12291227
images_uploaded_paths, images_not_uploaded_paths, duplicate_images_paths = upload_images_to_project(
1230-
project,
1228+
(project, project_folder),
12311229
images_to_upload,
12321230
annotation_status=annotation_status,
12331231
image_quality_in_editor=image_quality_in_editor
@@ -1313,7 +1311,7 @@ def upload_images_from_google_cloud_to_project(
13131311
path_to_url[str(image_save_pth)] = image_blob.name
13141312
images_to_upload.append(image_save_pth)
13151313
images_uploaded_paths, images_not_uploaded_paths, duplicate_images_paths = upload_images_to_project(
1316-
project,
1314+
(project, project_folder),
13171315
images_to_upload,
13181316
annotation_status=annotation_status,
13191317
image_quality_in_editor=image_quality_in_editor
@@ -1409,7 +1407,7 @@ def upload_images_from_azure_blob_to_project(
14091407
path_to_url[str(image_save_pth)] = image_blob.name
14101408
images_to_upload.append(image_save_pth)
14111409
images_uploaded_paths, images_not_uploaded_paths, duplicate_images_paths = upload_images_to_project(
1412-
project,
1410+
(project, project_folder),
14131411
images_to_upload,
14141412
annotation_status=annotation_status,
14151413
image_quality_in_editor=image_quality_in_editor
@@ -2229,11 +2227,10 @@ def get_project_default_image_quality_in_editor(project):
22292227
for setting in get_project_settings(project):
22302228
if "attribute" in setting and setting["attribute"] == "ImageQuality":
22312229
return setting["value"]
2232-
else:
2233-
raise SABaseException(
2234-
0,
2235-
"Image quality in editor should be 'compressed', 'original' or None for project settings value"
2236-
)
2230+
raise SABaseException(
2231+
0,
2232+
"Image quality in editor should be 'compressed', 'original' or None for project settings value"
2233+
)
22372234

22382235

22392236
def get_project_metadata(

superannotate/ml/ml_funcs.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,15 @@ def run_training(
228228
raise SABaseException(0, "Invalid project types")
229229
project_type = types.pop()
230230

231-
upload_state = upload_state_int_to_str(project.get("upload_state"))
232-
if upload_state == "External":
233-
raise SABaseException(
234-
0,
235-
"The function does not support projects containing images attached with URLs"
231+
for single_project in project:
232+
upload_state = upload_state_int_to_str(
233+
single_project.get("upload_state")
236234
)
235+
if upload_state == "External":
236+
raise SABaseException(
237+
0,
238+
"The function does not support projects containing images attached with URLs"
239+
)
237240

238241
base_model = base_model.get(project_type, None)
239242
if not base_model:

superannotate/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "4.1.1b4"
1+
__version__ = "4.1.2"

0 commit comments

Comments
 (0)